| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // -*- mode: C++; c-file-style: "cc-mode" -*- | ||
| 2 | //************************************************************************* | ||
| 3 | // DESCRIPTION: Verilator: Expression width calculations | ||
| 4 | // | ||
| 5 | // Code available from: https://verilator.org | ||
| 6 | // | ||
| 7 | //************************************************************************* | ||
| 8 | // | ||
| 9 | // Copyright 2003-2025 by Wilson Snyder. This program is free software; you | ||
| 10 | // can redistribute it and/or modify it under the terms of either the GNU | ||
| 11 | // Lesser General Public License Version 3 or the Perl Artistic License | ||
| 12 | // Version 2.0. | ||
| 13 | // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 | ||
| 14 | // | ||
| 15 | //************************************************************************* | ||
| 16 | // V3Width's Transformations: | ||
| 17 | // Top down traversal: | ||
| 18 | // Determine width of sub-expressions | ||
| 19 | // width() = # bits upper expression wants, 0 for anything-goes | ||
| 20 | // widthUnsized() = # bits for unsized constant, or 0 if it's sized | ||
| 21 | // widthMin() = Alternative acceptable width for linting, or width() if sized | ||
| 22 | // Determine this subop's width, can be either: | ||
| 23 | // Fixed width X | ||
| 24 | // Unsized, min width X ('d5 is unsized, min 3 bits.) | ||
| 25 | // Pass up: | ||
| 26 | // width() = # bits this expression generates | ||
| 27 | // widthSized() = true if all constants sized, else false | ||
| 28 | // Compute size of this expression | ||
| 29 | // Lint warn about mismatches | ||
| 30 | // If expr size != subop fixed, bad | ||
| 31 | // If expr size < subop unsized minimum, bad | ||
| 32 | // If expr size != subop, edit netlist | ||
| 33 | // For == and similar ops, if multibit underneath, add a REDOR | ||
| 34 | // If subop larger, add a EXTRACT | ||
| 35 | // If subop smaller, add a EXTEND | ||
| 36 | // Pass size to sub-expressions if required (+/-* etc) | ||
| 37 | // FINAL = true. | ||
| 38 | // Subexpressions lint and extend as needed | ||
| 39 | // | ||
| 40 | //************************************************************************* | ||
| 41 | // Signedness depends on: | ||
| 42 | // Decimal numbers are signed | ||
| 43 | // Based numbers are unsigned unless 's' prefix | ||
| 44 | // Comparison results are unsigned | ||
| 45 | // Bit&Part selects are unsigned, even if whole | ||
| 46 | // Concatenates are unsigned | ||
| 47 | // Ignore signedness of self-determined: | ||
| 48 | // shift rhs, ** rhs, x?: lhs, concat and replicate members | ||
| 49 | // Else, if any operand unsigned, output unsigned | ||
| 50 | // | ||
| 51 | // Real number rules: | ||
| 52 | // Real numbers are real (duh) | ||
| 53 | // Reals convert to integers by rounding | ||
| 54 | // Reals init to 0.0 | ||
| 55 | // Logicals convert compared to zero | ||
| 56 | // If any operand is real, result is real | ||
| 57 | //************************************************************************* | ||
| 58 | // V3Width is the only visitor that uses vup. We could switch to using userp, | ||
| 59 | // though note some iterators operate on next() and so would need to pass the | ||
| 60 | // same value on each nextp(). | ||
| 61 | //************************************************************************* | ||
| 62 | // See notes in internal.txt about misuse of iterateAndNext and use of | ||
| 63 | // iterateSubtreeReturnEdits. | ||
| 64 | //************************************************************************* | ||
| 65 | |||
| 66 | #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT | ||
| 67 | |||
| 68 | #include "V3Width.h" | ||
| 69 | |||
| 70 | #include "V3Ast.h" | ||
| 71 | #include "V3Begin.h" | ||
| 72 | #include "V3Const.h" | ||
| 73 | #include "V3Error.h" | ||
| 74 | #include "V3Global.h" | ||
| 75 | #include "V3LinkLValue.h" | ||
| 76 | #include "V3MemberMap.h" | ||
| 77 | #include "V3Number.h" | ||
| 78 | #include "V3Randomize.h" | ||
| 79 | #include "V3String.h" | ||
| 80 | #include "V3Task.h" | ||
| 81 | #include "V3WidthCommit.h" | ||
| 82 | |||
| 83 | // More code; this file was getting too large; see actions there | ||
| 84 | #define VERILATOR_V3WIDTH_CPP_ | ||
| 85 | #include "V3WidthRemove.h" | ||
| 86 | |||
| 87 |
10/19✓ Branch 0 taken 1455 times.
✓ Branch 1 taken 2493843 times.
✓ Branch 2 taken 485 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1455 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 485 times.
✓ Branch 8 taken 970 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 485 times.
✓ Branch 11 taken 970 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 485 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 485 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
4997386 | VL_DEFINE_DEBUG_FUNCTIONS; |
| 88 | |||
| 89 | //###################################################################### | ||
| 90 | |||
| 91 | enum Stage : uint8_t { | ||
| 92 | PRELIM = 1, | ||
| 93 | FINAL = 2, | ||
| 94 | BOTH = 3 | ||
| 95 | }; // Numbers are a bitmask <0>=prelim, <1>=final | ||
| 96 | ✗ | std::ostream& operator<<(std::ostream& str, const Stage& rhs) { | |
| 97 | ✗ | return str << ("-PFB"[static_cast<int>(rhs)]); | |
| 98 | } | ||
| 99 | |||
| 100 | enum Determ : uint8_t { | ||
| 101 | SELF, // Self-determined | ||
| 102 | CONTEXT_DET, // Context-determined | ||
| 103 | ASSIGN // Assignment-like where sign comes from RHS only | ||
| 104 | }; | ||
| 105 | ✗ | std::ostream& operator<<(std::ostream& str, const Determ& rhs) { | |
| 106 | static const char* const s_det[] = {"SELF", "CNTX", "ASSN"}; | ||
| 107 | ✗ | return str << s_det[rhs]; | |
| 108 | } | ||
| 109 | |||
| 110 | #define v3widthWarn(lhs, rhs, msg) \ | ||
| 111 | v3errorEnd( \ | ||
| 112 | v3errorBuildMessage(V3Error::v3errorPrep((lhs) < (rhs) ? V3ErrorCode::WIDTHTRUNC \ | ||
| 113 | : (lhs) > (rhs) ? V3ErrorCode::WIDTHEXPAND \ | ||
| 114 | : V3ErrorCode::WIDTH), \ | ||
| 115 | msg)) | ||
| 116 | |||
| 117 | //###################################################################### | ||
| 118 | // Width state, as a visitor of each AstNode | ||
| 119 | |||
| 120 | class WidthVP final { | ||
| 121 | // Parameters to pass down hierarchy with visit functions. | ||
| 122 | AstNodeDType* const m_dtypep; // Parent's data type to resolve to | ||
| 123 | const Stage m_stage; // If true, report errors | ||
| 124 | public: | ||
| 125 | WidthVP(AstNodeDType* dtypep, Stage stage) | ||
| 126 | 1597637 | : m_dtypep{dtypep} | |
| 127 |
3/34✓ Branch 1 taken 819347 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 7 taken 133885 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 233881 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
1396959 | , m_stage{stage} { |
| 128 | // Prelim doesn't look at assignments, so shouldn't need a dtype, | ||
| 129 | // however AstPattern uses them | ||
| 130 | } | ||
| 131 | 3077603 | WidthVP(Determ determ, Stage stage) | |
| 132 | 3077603 | : m_dtypep{nullptr} | |
| 133 | 3077603 | , m_stage{stage} { | |
| 134 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3077603 times.
|
3077603 | if (determ != SELF && stage != PRELIM) |
| 135 | ✗ | v3fatalSrc("Context-determined width request only allowed as prelim step"); | |
| 136 | 3077603 | } | |
| 137 | WidthVP* p() { return this; } | ||
| 138 | ✗ | bool selfDtm() const { return m_dtypep == nullptr; } | |
| 139 | ✗ | AstNodeDType* dtypep() const { | |
| 140 | // Detect where overrideDType is probably the intended call | ||
| 141 | ✗ | UASSERT(m_dtypep, "Width dtype request on self-determined or preliminary VUP"); | |
| 142 | ✗ | return m_dtypep; | |
| 143 | } | ||
| 144 | 419875 | AstNodeDType* dtypeNullp() const { return m_dtypep; } | |
| 145 | AstNodeDType* dtypeNullSkipRefp() const { | ||
| 146 | AstNodeDType* dtp = dtypeNullp(); | ||
| 147 | ✗ | if (dtp) dtp = dtp->skipRefp(); | |
| 148 | return dtp; | ||
| 149 | } | ||
| 150 | 171432 | AstNodeDType* dtypeOverridep(AstNodeDType* defaultp) const { | |
| 151 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 171432 times.
|
171432 | UASSERT(m_stage != PRELIM, "Parent dtype should be a final-stage action"); |
| 152 |
2/2✓ Branch 0 taken 133503 times.
✓ Branch 1 taken 37929 times.
|
171432 | return m_dtypep ? m_dtypep : defaultp; |
| 153 | } | ||
| 154 | int width() const { | ||
| 155 | UASSERT(m_dtypep, "Width request on self-determined or preliminary VUP"); | ||
| 156 | return m_dtypep->width(); | ||
| 157 | } | ||
| 158 | int widthMin() const { | ||
| 159 | UASSERT(m_dtypep, "Width request on self-determined or preliminary VUP"); | ||
| 160 | return m_dtypep->widthMin(); | ||
| 161 | } | ||
| 162 | 2046827 | bool prelim() const { return m_stage & PRELIM; } | |
| 163 | 558499 | bool final() const { return m_stage & FINAL; } | |
| 164 | ✗ | void dump(std::ostream& str) const { | |
| 165 | ✗ | if (!m_dtypep) { | |
| 166 | ✗ | str << " VUP(s=" << m_stage << ",self)"; | |
| 167 | } else { | ||
| 168 | ✗ | str << " VUP(s=" << m_stage << ",dt=" << cvtToHex(dtypep()); | |
| 169 | ✗ | dtypep()->dumpSmall(str); | |
| 170 | ✗ | str << ")"; | |
| 171 | } | ||
| 172 | } | ||
| 173 | }; | ||
| 174 | ✗ | std::ostream& operator<<(std::ostream& str, const WidthVP* vup) { | |
| 175 | ✗ | if (vup) vup->dump(str); | |
| 176 | ✗ | return str; | |
| 177 | } | ||
| 178 | |||
| 179 | //###################################################################### | ||
| 180 | |||
| 181 | class WidthClearVisitor final { | ||
| 182 | // Rather than a VNVisitor, can just quickly touch every node | ||
| 183 | 2026062 | void clearWidthRecurse(AstNode* nodep) { | |
| 184 |
2/2✓ Branch 0 taken 2245125 times.
✓ Branch 1 taken 2026062 times.
|
4271187 | for (; nodep; nodep = nodep->nextp()) { |
| 185 | nodep->didWidth(false); | ||
| 186 |
2/2✓ Branch 0 taken 1121408 times.
✓ Branch 1 taken 1123717 times.
|
2245125 | if (nodep->op1p()) clearWidthRecurse(nodep->op1p()); |
| 187 |
2/2✓ Branch 0 taken 653024 times.
✓ Branch 1 taken 1592101 times.
|
2245125 | if (nodep->op2p()) clearWidthRecurse(nodep->op2p()); |
| 188 |
2/2✓ Branch 0 taken 171333 times.
✓ Branch 1 taken 2073792 times.
|
2245125 | if (nodep->op3p()) clearWidthRecurse(nodep->op3p()); |
| 189 |
2/2✓ Branch 0 taken 79812 times.
✓ Branch 1 taken 2165313 times.
|
2245125 | if (nodep->op4p()) clearWidthRecurse(nodep->op4p()); |
| 190 | } | ||
| 191 | 2026062 | } | |
| 192 | |||
| 193 | public: | ||
| 194 | // CONSTRUCTORS | ||
| 195 | 485 | explicit WidthClearVisitor(AstNetlist* nodep) { clearWidthRecurse(nodep); } | |
| 196 | 485 | virtual ~WidthClearVisitor() = default; | |
| 197 | }; | ||
| 198 | |||
| 199 | //###################################################################### | ||
| 200 | |||
| 201 | #define accept in_WidthVisitor_use_AstNode_iterate_instead_of_AstNode_accept | ||
| 202 | |||
| 203 | //###################################################################### | ||
| 204 | |||
| 205 | class WidthVisitor final : public VNVisitor { | ||
| 206 | // TYPES | ||
| 207 | using TableMap = std::map<std::pair<const AstNodeDType*, VAttrType>, AstVar*>; | ||
| 208 | using PatVecMap = std::map<int, AstPatMember*>; | ||
| 209 | using DTypeMap = std::map<const std::string, AstPatMember*>; | ||
| 210 | |||
| 211 | // STATE | ||
| 212 | VMemberMap m_memberMap; // Member names cached for fast lookup | ||
| 213 | V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects | ||
| 214 | WidthVP* m_vup = nullptr; // Current node state | ||
| 215 | const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations | ||
| 216 | const AstEnumItem* m_enumItemp = nullptr; // Current enum item | ||
| 217 | const AstNodeFTask* m_ftaskp = nullptr; // Current function/task | ||
| 218 | const AstConstraint* m_constraintp = nullptr; // Current constraint | ||
| 219 | const AstNodeProcedure* m_procedurep = nullptr; // Current final/always | ||
| 220 | const AstWith* m_withp = nullptr; // Current 'with' statement | ||
| 221 | const AstFunc* m_funcp = nullptr; // Current function | ||
| 222 | const AstAttrOf* m_attrp = nullptr; // Current attribute | ||
| 223 | const AstNodeExpr* m_randomizeFromp = nullptr; // Current randomize method call fromp | ||
| 224 | const bool m_paramsOnly; // Computing parameter value; limit operation | ||
| 225 | const bool m_doGenerate; // Do errors later inside generate statement | ||
| 226 | int m_dtTables = 0; // Number of created data type tables | ||
| 227 | TableMap m_tableMap; // Created tables so can remove duplicates | ||
| 228 | std::map<const AstNodeDType*, AstQueueDType*> | ||
| 229 | m_queueDTypeIndexed; // Queues with given index type | ||
| 230 | |||
| 231 | static constexpr int ENUM_LOOKUP_BITS = 16; // Maximum # bits to make enum lookup table | ||
| 232 | |||
| 233 | // ENUMS | ||
| 234 | enum ExtendRule : uint8_t { | ||
| 235 | EXTEND_EXP, // Extend if expect sign and node signed, e.g. node=y in ADD(x,y), "x + y" | ||
| 236 | EXTEND_ZERO, // Extend with zeros. e.g. node=y in EQ(x,y), "x == y" | ||
| 237 | EXTEND_LHS, // Extend with sign if node signed. e.g. node=y in ASSIGN(y,x), "x = y" | ||
| 238 | EXTEND_OFF // No extension | ||
| 239 | }; | ||
| 240 | |||
| 241 | // VISITORS | ||
| 242 | // Naming: width_O{outputtype}_L{lhstype}_R{rhstype}_W{widthing}_S{signing} | ||
| 243 | // Where type: | ||
| 244 | // _O1=boolean (width 1 unsigned) | ||
| 245 | // _Ou=unsigned | ||
| 246 | // _Os=signed | ||
| 247 | // _Ous=unsigned or signed | ||
| 248 | // _Or=real | ||
| 249 | // _Ox=anything | ||
| 250 | |||
| 251 | // Widths: 1 bit out, lhs 1 bit; Real: converts via compare with 0 | ||
| 252 | 76776 | void visit(AstLogNot* nodep) override { visit_log_not(nodep); } | |
| 253 | // Widths: 1 bit out, lhs 1 bit, rhs 1 bit; Real: converts via compare with 0 | ||
| 254 | 8433 | void visit(AstLogAnd* nodep) override { visit_log_and_or(nodep); } | |
| 255 | 8429 | void visit(AstLogOr* nodep) override { visit_log_and_or(nodep); } | |
| 256 | ✗ | void visit(AstLogEq* nodep) override { | |
| 257 | // Conversion from real not in IEEE, but a fallout | ||
| 258 | ✗ | visit_log_and_or(nodep); | |
| 259 | } | ||
| 260 | ✗ | void visit(AstLogIf* nodep) override { | |
| 261 | // Conversion from real not in IEEE, but a fallout | ||
| 262 | ✗ | visit_log_and_or(nodep); | |
| 263 | } | ||
| 264 | |||
| 265 | // Widths: 1 bit out, Any width lhs | ||
| 266 | 23761 | void visit(AstRedAnd* nodep) override { visit_red_and_or(nodep); } | |
| 267 | 24169 | void visit(AstRedOr* nodep) override { visit_red_and_or(nodep); } | |
| 268 | 31444 | void visit(AstRedXor* nodep) override { visit_red_and_or(nodep); } | |
| 269 | ✗ | void visit(AstOneHot* nodep) override { visit_red_and_or(nodep); } | |
| 270 | ✗ | void visit(AstOneHot0* nodep) override { visit_red_and_or(nodep); } | |
| 271 | ✗ | void visit(AstIsUnknown* nodep) override { | |
| 272 | ✗ | visit_red_unknown(nodep); // Allow real | |
| 273 | } | ||
| 274 | |||
| 275 | // These have different node types, as they operate differently | ||
| 276 | // Must add to case statement below, | ||
| 277 | // Widths: 1 bit out, lhs width == rhs width. real if lhs|rhs real | ||
| 278 | 8448 | void visit(AstEq* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 279 | 8390 | void visit(AstNeq* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 280 | 8124 | void visit(AstGt* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 281 | 8240 | void visit(AstGte* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 282 | 8274 | void visit(AstLt* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 283 | 8029 | void visit(AstLte* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 284 | 555 | void visit(AstGtS* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 285 | 503 | void visit(AstGteS* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 286 | 528 | void visit(AstLtS* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 287 | 503 | void visit(AstLteS* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 288 | ✗ | void visit(AstEqCase* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 289 | ✗ | void visit(AstNeqCase* nodep) override { visit_cmp_eq_gt(nodep, true); } | |
| 290 | // ... These comparisons don't allow reals | ||
| 291 | ✗ | void visit(AstEqWild* nodep) override { visit_cmp_eq_gt(nodep, false); } | |
| 292 | ✗ | void visit(AstNeqWild* nodep) override { visit_cmp_eq_gt(nodep, false); } | |
| 293 | // ... Real compares | ||
| 294 | ✗ | void visit(AstEqD* nodep) override { visit_cmp_real(nodep); } | |
| 295 | ✗ | void visit(AstNeqD* nodep) override { visit_cmp_real(nodep); } | |
| 296 | ✗ | void visit(AstLtD* nodep) override { visit_cmp_real(nodep); } | |
| 297 | ✗ | void visit(AstLteD* nodep) override { visit_cmp_real(nodep); } | |
| 298 | ✗ | void visit(AstGtD* nodep) override { visit_cmp_real(nodep); } | |
| 299 | ✗ | void visit(AstGteD* nodep) override { visit_cmp_real(nodep); } | |
| 300 | // ... String compares | ||
| 301 | ✗ | void visit(AstEqN* nodep) override { visit_cmp_string(nodep); } | |
| 302 | ✗ | void visit(AstNeqN* nodep) override { visit_cmp_string(nodep); } | |
| 303 | ✗ | void visit(AstLtN* nodep) override { visit_cmp_string(nodep); } | |
| 304 | ✗ | void visit(AstLteN* nodep) override { visit_cmp_string(nodep); } | |
| 305 | ✗ | void visit(AstGtN* nodep) override { visit_cmp_string(nodep); } | |
| 306 | ✗ | void visit(AstGteN* nodep) override { visit_cmp_string(nodep); } | |
| 307 | // ... Data type compares | ||
| 308 | ✗ | void visit(AstEqT* nodep) override { visit_cmp_type(nodep); } | |
| 309 | ✗ | void visit(AstNeqT* nodep) override { visit_cmp_type(nodep); } | |
| 310 | |||
| 311 | // Widths: out width = lhs width = rhs width | ||
| 312 | // Signed: Output signed iff LHS & RHS signed. | ||
| 313 | // Real: Not allowed | ||
| 314 | 8423 | void visit(AstAnd* nodep) override { visit_boolexpr_and_or(nodep); } | |
| 315 | 8310 | void visit(AstOr* nodep) override { visit_boolexpr_and_or(nodep); } | |
| 316 | 26664 | void visit(AstXor* nodep) override { visit_boolexpr_and_or(nodep); } | |
| 317 | ✗ | void visit(AstBufIf1* nodep) override { | |
| 318 | ✗ | visit_boolexpr_and_or(nodep); | |
| 319 | } // Signed behavior changing in 3.814 | ||
| 320 | // Width: Max(Lhs,Rhs) sort of. | ||
| 321 | // Real: If either side real | ||
| 322 | // Signed: If both sides real | ||
| 323 | 8385 | void visit(AstAdd* nodep) override { visit_add_sub_replace(nodep, true); } | |
| 324 | 8380 | void visit(AstSub* nodep) override { visit_add_sub_replace(nodep, true); } | |
| 325 | ✗ | void visit(AstDiv* nodep) override { visit_add_sub_replace(nodep, true); } | |
| 326 | 8415 | void visit(AstMul* nodep) override { visit_add_sub_replace(nodep, true); } | |
| 327 | // These can't promote to real | ||
| 328 | ✗ | void visit(AstModDiv* nodep) override { visit_add_sub_replace(nodep, false); } | |
| 329 | ✗ | void visit(AstModDivS* nodep) override { visit_add_sub_replace(nodep, false); } | |
| 330 | 2 | void visit(AstMulS* nodep) override { visit_add_sub_replace(nodep, false); } | |
| 331 | ✗ | void visit(AstDivS* nodep) override { visit_add_sub_replace(nodep, false); } | |
| 332 | // Widths: out width = lhs width, but upper matters | ||
| 333 | // Signed: Output signed iff LHS signed; unary operator | ||
| 334 | // Unary promote to real | ||
| 335 | 15508 | void visit(AstNegate* nodep) override { visit_negate_not(nodep, true); } | |
| 336 | // Unary never real | ||
| 337 | 32259 | void visit(AstNot* nodep) override { visit_negate_not(nodep, false); } | |
| 338 | |||
| 339 | // Real: inputs and output real | ||
| 340 | ✗ | void visit(AstAddD* nodep) override { visit_real_add_sub(nodep); } | |
| 341 | ✗ | void visit(AstSubD* nodep) override { visit_real_add_sub(nodep); } | |
| 342 | ✗ | void visit(AstDivD* nodep) override { visit_real_add_sub(nodep); } | |
| 343 | ✗ | void visit(AstMulD* nodep) override { visit_real_add_sub(nodep); } | |
| 344 | ✗ | void visit(AstPowD* nodep) override { visit_real_add_sub(nodep); } | |
| 345 | ✗ | void visit(AstNodeSystemBiopD* nodep) override { visit_real_add_sub(nodep); } | |
| 346 | // Real: Output real | ||
| 347 | ✗ | void visit(AstNegateD* nodep) override { visit_real_neg_ceil(nodep); } | |
| 348 | ✗ | void visit(AstNodeSystemUniopD* nodep) override { visit_real_neg_ceil(nodep); } | |
| 349 | |||
| 350 | // Widths: out signed/unsigned width = lhs width, input un|signed | ||
| 351 | 148628 | void visit(AstSigned* nodep) override { visit_signed_unsigned(nodep, VSigning::SIGNED); } | |
| 352 | 148132 | void visit(AstUnsigned* nodep) override { visit_signed_unsigned(nodep, VSigning::UNSIGNED); } | |
| 353 | |||
| 354 | // Widths: Output width from lhs, rhs<33 bits | ||
| 355 | // Signed: If lhs signed | ||
| 356 | 16799 | void visit(AstShiftL* nodep) override { visit_shift(nodep); } | |
| 357 | 8376 | void visit(AstShiftR* nodep) override { visit_shift(nodep); } | |
| 358 | // ShiftRS converts to ShiftR, but not vice-versa | ||
| 359 | 8564 | void visit(AstShiftRS* nodep) override { visit_shift(nodep); } | |
| 360 | |||
| 361 | //======== | ||
| 362 | // Widths: Output real, input integer signed | ||
| 363 | ✗ | void visit(AstBitsToRealD* nodep) override { visit_Or_Lu64(nodep); } | |
| 364 | |||
| 365 | // Widths: Output integer signed, input real | ||
| 366 | ✗ | void visit(AstRToIS* nodep) override { visit_Os32_Lr(nodep); } | |
| 367 | ✗ | void visit(AstRToIRoundS* nodep) override { | |
| 368 | // Only created here, size comes from upper expression | ||
| 369 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 370 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 371 | } | ||
| 372 | ✗ | UASSERT_OBJ(nodep->dtypep()->widthSized(), nodep, "RToIRoundS should be presized"); | |
| 373 | } | ||
| 374 | |||
| 375 | // Widths: Output integer unsigned, input real | ||
| 376 | ✗ | void visit(AstRealToBits* nodep) override { visit_Ou64_Lr(nodep); } | |
| 377 | |||
| 378 | // Output integer, input string | ||
| 379 | ✗ | void visit(AstLenN* nodep) override { | |
| 380 | // Widths: 32 bit out | ||
| 381 | ✗ | UASSERT_OBJ(nodep->lhsp(), nodep, "For unary ops only!"); | |
| 382 | ✗ | if (m_vup->prelim()) { | |
| 383 | // See similar handling in visit_cmp_eq_gt where created | ||
| 384 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 385 | ✗ | nodep->dtypeSetSigned32(); | |
| 386 | } | ||
| 387 | } | ||
| 388 | ✗ | void visit(AstPutcN* nodep) override { | |
| 389 | // CALLER: str.putc() | ||
| 390 | ✗ | UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!"); | |
| 391 | ✗ | if (m_vup && m_vup->prelim()) { | |
| 392 | // See similar handling in visit_cmp_eq_gt where created | ||
| 393 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 394 | ✗ | iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 395 | ✗ | iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH); | |
| 396 | ✗ | nodep->dtypeSetString(); // AstPutcN returns the new string to be assigned by | |
| 397 | // AstAssign | ||
| 398 | } | ||
| 399 | } | ||
| 400 | ✗ | void visit(AstGetcN* nodep) override { | |
| 401 | // CALLER: str.getc() | ||
| 402 | ✗ | UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); | |
| 403 | ✗ | if (m_vup && m_vup->prelim()) { | |
| 404 | // See similar handling in visit_cmp_eq_gt where created | ||
| 405 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 406 | ✗ | iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 407 | ✗ | nodep->dtypeSetBitSized(8, VSigning::UNSIGNED); | |
| 408 | } | ||
| 409 | } | ||
| 410 | ✗ | void visit(AstGetcRefN* nodep) override { | |
| 411 | // CALLER: str.getc() | ||
| 412 | ✗ | UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); | |
| 413 | ✗ | if (m_vup && m_vup->prelim()) { | |
| 414 | // See similar handling in visit_cmp_eq_gt where created | ||
| 415 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 416 | ✗ | iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 417 | ✗ | nodep->dtypeSetBitSized(8, VSigning::UNSIGNED); | |
| 418 | } | ||
| 419 | } | ||
| 420 | ✗ | void visit(AstSubstrN* nodep) override { | |
| 421 | // CALLER: str.substr() | ||
| 422 | ✗ | UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!"); | |
| 423 | ✗ | if (m_vup && m_vup->prelim()) { | |
| 424 | // See similar handling in visit_cmp_eq_gt where created | ||
| 425 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 426 | ✗ | iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 427 | ✗ | iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH); | |
| 428 | ✗ | nodep->dtypeSetString(); | |
| 429 | } | ||
| 430 | } | ||
| 431 | ✗ | void visit(AstCompareNN* nodep) override { | |
| 432 | // CALLER: str.compare(), str.icompare() | ||
| 433 | // Widths: 32 bit out | ||
| 434 | ✗ | UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); | |
| 435 | ✗ | if (m_vup->prelim()) { | |
| 436 | // See similar handling in visit_cmp_eq_gt where created | ||
| 437 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 438 | ✗ | iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 439 | ✗ | nodep->dtypeSetSigned32(); | |
| 440 | } | ||
| 441 | } | ||
| 442 | ✗ | void visit(AstAtoN* nodep) override { | |
| 443 | // CALLER: str.atobin(), atoi(), atohex(), atooct(), atoreal() | ||
| 444 | // Width: 64bit floating point for atoreal(), 32bit out for the others | ||
| 445 | ✗ | if (m_vup->prelim()) { | |
| 446 | // See similar handling in visit_cmp_eq_gt where created | ||
| 447 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 448 | ✗ | if (nodep->format() == AstAtoN::ATOREAL) { | |
| 449 | ✗ | nodep->dtypeSetDouble(); | |
| 450 | } else { | ||
| 451 | ✗ | nodep->dtypeSetSigned32(); | |
| 452 | } | ||
| 453 | } | ||
| 454 | } | ||
| 455 | ✗ | void visit(AstNToI* nodep) override { | |
| 456 | // Created here, should be already sized | ||
| 457 | ✗ | if (m_vup->prelim()) { | |
| 458 | ✗ | UASSERT_OBJ(nodep->dtypep(), nodep, "NToI should be sized when created"); | |
| 459 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | // Widths: Constant, terminal | ||
| 464 | ✗ | void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); } | |
| 465 | ✗ | void visit(AstTimeD* nodep) override { nodep->dtypeSetDouble(); } | |
| 466 | ✗ | void visit(AstTimePrecision* nodep) override { nodep->dtypeSetSigned32(); } | |
| 467 | ✗ | void visit(AstTimeUnit* nodep) override { | |
| 468 | ✗ | nodep->replaceWith( | |
| 469 | ✗ | new AstConst{nodep->fileline(), AstConst::Signed32{}, nodep->timeunit().powerOfTen()}); | |
| 470 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 471 | } | ||
| 472 | ✗ | void visit(AstScopeName* nodep) override { | |
| 473 | ✗ | nodep->dtypeSetUInt64(); // A pointer, but not that it matters | |
| 474 | } | ||
| 475 | |||
| 476 | 169609 | void visit(AstNodeCond* nodep) override { | |
| 477 | // op = cond ? expr1 : expr2 | ||
| 478 | // See IEEE-2012 11.4.11 and Table 11-21. | ||
| 479 | // LHS is self-determined | ||
| 480 | // Width: max(RHS, THS) | ||
| 481 | // Signed: Output signed iff RHS & THS signed (presumed, not in IEEE) | ||
| 482 | // Real: Output real if either expression is real, non-real argument gets converted | ||
| 483 |
2/2✓ Branch 0 taken 91290 times.
✓ Branch 1 taken 78319 times.
|
169609 | if (m_vup->prelim()) { // First stage evaluation |
| 484 | // Just once, do the conditional, expect one bit out. | ||
| 485 | 91290 | iterateCheckBool(nodep, "Conditional Test", nodep->condp(), BOTH); | |
| 486 | // Determine sub expression widths only relying on what's in the subops | ||
| 487 | // CONTEXT_DET determined, but need data type for pattern assignments | ||
| 488 | 91290 | userIterateAndNext(nodep->thenp(), WidthVP{m_vup->dtypeNullp(), PRELIM}.p()); | |
| 489 |
2/2✓ Branch 1 taken 10133 times.
✓ Branch 2 taken 81157 times.
|
91290 | userIterateAndNext(nodep->elsep(), WidthVP{m_vup->dtypeNullp(), PRELIM}.p()); |
| 490 | // Calculate width of this expression. | ||
| 491 | // First call (prelim()) m_vup->width() is probably zero, so we'll return | ||
| 492 | // the size of this subexpression only. | ||
| 493 | // Second call (final()) m_vup->width() is probably the expression size, so | ||
| 494 | // the expression includes the size of the output too. | ||
| 495 | const AstNodeDType* const thenDTypep = nodep->thenp()->dtypep(); | ||
| 496 | const AstNodeDType* const elseDTypep = nodep->elsep()->dtypep(); | ||
| 497 |
2/4✓ Branch 0 taken 10133 times.
✓ Branch 1 taken 81157 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
91290 | if (nodep->thenp()->isNull() && nodep->elsep()->isNull()) { |
| 498 | ✗ | nodep->dtypep(m_vup->dtypeNullp()); | |
| 499 |
2/2✓ Branch 0 taken 12130 times.
✓ Branch 1 taken 79160 times.
|
91290 | } else if (thenDTypep->skipRefp() == elseDTypep->skipRefp()) { |
| 500 | // TODO might need a broader equation, use the Castable function? | ||
| 501 | nodep->dtypeFrom(thenDTypep); | ||
| 502 | 79160 | } else if (nodep->thenp()->isClassHandleValue() | |
| 503 |
2/4✓ Branch 0 taken 79160 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 79160 times.
✗ Branch 4 not taken.
|
79160 | || nodep->elsep()->isClassHandleValue()) { |
| 504 | AstNodeDType* commonClassTypep = nullptr; | ||
| 505 | ✗ | if (nodep->thenp()->isClassHandleValue() && nodep->elsep()->isClassHandleValue()) { | |
| 506 | // Get the most-deriving class type that both arguments can be casted to. | ||
| 507 | commonClassTypep | ||
| 508 | ✗ | = AstNode::getCommonClassTypep(nodep->thenp(), nodep->elsep()); | |
| 509 | } | ||
| 510 | ✗ | if (commonClassTypep) { | |
| 511 | nodep->dtypep(commonClassTypep); | ||
| 512 | } else { | ||
| 513 | ✗ | nodep->v3error("Incompatible types of operands of condition operator: " | |
| 514 | << thenDTypep->prettyTypeName() << " and " | ||
| 515 | << elseDTypep->prettyTypeName()); | ||
| 516 | nodep->dtypeFrom(thenDTypep); | ||
| 517 | } | ||
| 518 |
2/4✓ Branch 1 taken 79160 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 79160 times.
✗ Branch 5 not taken.
|
79160 | } else if (nodep->thenp()->isDouble() || nodep->elsep()->isDouble()) { |
| 519 | ✗ | nodep->dtypeSetDouble(); | |
| 520 |
2/4✓ Branch 1 taken 79160 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 79160 times.
✗ Branch 5 not taken.
|
79160 | } else if (nodep->thenp()->isString() || nodep->elsep()->isString()) { |
| 521 | ✗ | nodep->dtypeSetString(); | |
| 522 | } else { | ||
| 523 |
4/6✓ Branch 0 taken 79160 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38054 times.
✓ Branch 3 taken 41106 times.
✓ Branch 4 taken 79160 times.
✗ Branch 5 not taken.
|
196374 | const int width = std::max(nodep->thenp()->width(), nodep->elsep()->width()); |
| 524 | const int mwidth | ||
| 525 |
4/6✓ Branch 0 taken 79160 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38054 times.
✓ Branch 3 taken 41106 times.
✓ Branch 4 taken 79160 times.
✗ Branch 5 not taken.
|
196374 | = std::max(nodep->thenp()->widthMin(), nodep->elsep()->widthMin()); |
| 526 | const bool issigned = nodep->thenp()->isSigned() && nodep->elsep()->isSigned(); | ||
| 527 | 158320 | nodep->dtypeSetLogicUnsized(width, mwidth, VSigning::fromBool(issigned)); | |
| 528 | } | ||
| 529 | } | ||
| 530 |
2/2✓ Branch 0 taken 91290 times.
✓ Branch 1 taken 78319 times.
|
169609 | if (m_vup->final()) { |
| 531 | 91290 | AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); | |
| 532 | AstNodeDType* const subDTypep = expDTypep; | ||
| 533 | nodep->dtypep(expDTypep); | ||
| 534 | // Error report and change sizes for suboperands of this node. | ||
| 535 | 91290 | iterateCheck(nodep, "Conditional True", nodep->thenp(), CONTEXT_DET, FINAL, subDTypep, | |
| 536 | EXTEND_EXP); | ||
| 537 | 91290 | iterateCheck(nodep, "Conditional False", nodep->elsep(), CONTEXT_DET, FINAL, subDTypep, | |
| 538 | EXTEND_EXP); | ||
| 539 | } | ||
| 540 | 169609 | } | |
| 541 | 180034 | void visit(AstConcat* nodep) override { | |
| 542 | // Real: Not allowed (assumed) | ||
| 543 | // Signed: unsigned output, input either (assumed) | ||
| 544 | // IEEE-2012 Table 11-21, and 11.8.1: | ||
| 545 | // LHS, RHS is self-determined | ||
| 546 | // signed: Unsigned (11.8.1) | ||
| 547 | // width: LHS + RHS | ||
| 548 |
2/2✓ Branch 0 taken 90017 times.
✓ Branch 1 taken 90017 times.
|
180034 | AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp(); |
| 549 |
1/2✓ Branch 1 taken 90017 times.
✗ Branch 2 not taken.
|
360068 | userIterate(vdtypep, WidthVP{SELF, BOTH}.p()); |
| 550 | // Conversions | ||
| 551 | if (const AstDynArrayDType* const adtypep = VN_CAST(vdtypep, DynArrayDType)) { | ||
| 552 | // IEEE 1800-2023 10.10 requires looking at argument data type | ||
| 553 | // to determine if value or push | ||
| 554 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p()); | |
| 555 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p()); | |
| 556 | // Queue "element 0" is lhsp, so we need to swap arguments | ||
| 557 | const bool lhsIsValue | ||
| 558 | ✗ | = AstNode::computeCastable(adtypep->subDTypep(), nodep->lhsp()->dtypep(), nullptr) | |
| 559 | .isAssignable(); | ||
| 560 | const bool rhsIsValue | ||
| 561 | ✗ | = AstNode::computeCastable(adtypep->subDTypep(), nodep->rhsp()->dtypep(), nullptr) | |
| 562 | .isAssignable(); | ||
| 563 | AstConsDynArray* const newp | ||
| 564 | = new AstConsDynArray{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(), | ||
| 565 | ✗ | lhsIsValue, nodep->lhsp()->unlinkFrBack()}; | |
| 566 | ✗ | nodep->replaceWith(newp); | |
| 567 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 568 | ✗ | userIterateChildren(newp, m_vup); | |
| 569 | ✗ | return; | |
| 570 | } | ||
| 571 | if (const AstQueueDType* const adtypep = VN_CAST(vdtypep, QueueDType)) { | ||
| 572 | // IEEE 1800-2023 10.10 requires looking at argument data type | ||
| 573 | // to determine if value or push | ||
| 574 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p()); | |
| 575 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p()); | |
| 576 | const bool lhsIsValue | ||
| 577 | ✗ | = AstNode::computeCastable(adtypep->subDTypep(), nodep->lhsp()->dtypep(), nullptr) | |
| 578 | .isAssignable(); | ||
| 579 | const bool rhsIsValue | ||
| 580 | ✗ | = AstNode::computeCastable(adtypep->subDTypep(), nodep->rhsp()->dtypep(), nullptr) | |
| 581 | .isAssignable(); | ||
| 582 | AstConsQueue* const newp | ||
| 583 | = new AstConsQueue{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(), | ||
| 584 | ✗ | lhsIsValue, nodep->lhsp()->unlinkFrBack()}; | |
| 585 | ✗ | nodep->replaceWith(newp); | |
| 586 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 587 | ✗ | userIterateChildren(newp, m_vup); | |
| 588 | ✗ | return; | |
| 589 | } | ||
| 590 | if (VN_IS(vdtypep, UnpackArrayDType)) { | ||
| 591 | ✗ | auto* const newp = new AstPattern{nodep->fileline(), nullptr}; | |
| 592 | ✗ | patConcatConvertRecurse(newp, nodep); | |
| 593 | ✗ | nodep->replaceWith(newp); | |
| 594 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 595 | ✗ | userIterate(newp, m_vup); | |
| 596 | ✗ | return; | |
| 597 | } | ||
| 598 | |||
| 599 | // Concat handling | ||
| 600 |
2/2✓ Branch 0 taken 90017 times.
✓ Branch 1 taken 90017 times.
|
180034 | if (m_vup->prelim()) { |
| 601 | if (VN_IS(vdtypep, AssocArrayDType) // | ||
| 602 | || VN_IS(vdtypep, DynArrayDType) // | ||
| 603 | || VN_IS(vdtypep, QueueDType)) { | ||
| 604 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: Concatenation to form " | |
| 605 | << vdtypep->prettyDTypeNameQ() << " data type"); | ||
| 606 | } | ||
| 607 | |||
| 608 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 90017 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
90017 | if (vdtypep && vdtypep->isString()) { |
| 609 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 610 | ✗ | iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 611 | ✗ | nodep->dtypeSetString(); | |
| 612 | } else { | ||
| 613 | 90017 | iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); | |
| 614 | 90017 | iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); | |
| 615 |
2/4✓ Branch 0 taken 90017 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 90017 times.
✗ Branch 3 not taken.
|
360068 | nodep->dtypeSetLogicUnsized(nodep->lhsp()->width() + nodep->rhsp()->width(), |
| 616 | nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin(), | ||
| 617 | VSigning::UNSIGNED); | ||
| 618 | } | ||
| 619 | // Cleanup zero width Verilog2001 {x,{0{foo}}} now, | ||
| 620 | // otherwise having width(0) will cause later assertions to fire | ||
| 621 | if (const AstReplicate* const repp = VN_CAST(nodep->lhsp(), Replicate)) { | ||
| 622 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1719 times.
|
1719 | if (repp->width() == 0) { // Keep rhs |
| 623 | ✗ | nodep->replaceWith(nodep->rhsp()->unlinkFrBack()); | |
| 624 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 625 | ✗ | return; | |
| 626 | } | ||
| 627 | } | ||
| 628 | if (const AstReplicate* const repp = VN_CAST(nodep->rhsp(), Replicate)) { | ||
| 629 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1716 times.
|
1716 | if (repp->width() == 0) { // Keep lhs |
| 630 | ✗ | nodep->replaceWith(nodep->lhsp()->unlinkFrBack()); | |
| 631 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 632 | ✗ | return; | |
| 633 | } | ||
| 634 | } | ||
| 635 | } | ||
| 636 |
2/2✓ Branch 0 taken 90017 times.
✓ Branch 1 taken 90017 times.
|
180034 | if (m_vup->final()) { |
| 637 |
2/4✓ Branch 1 taken 90017 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 90017 times.
✗ Branch 5 not taken.
|
90017 | if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) { |
| 638 | AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack(); | ||
| 639 | AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack(); | ||
| 640 | ✗ | if (!lhsp->isString()) lhsp = new AstCvtPackString{lhsp->fileline(), lhsp}; | |
| 641 | ✗ | if (!rhsp->isString()) rhsp = new AstCvtPackString{rhsp->fileline(), rhsp}; | |
| 642 | ✗ | AstNode* const newp = new AstConcatN{nodep->fileline(), lhsp, rhsp}; | |
| 643 | ✗ | nodep->replaceWith(newp); | |
| 644 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 645 | ✗ | return; | |
| 646 | } | ||
| 647 | if (!nodep->dtypep()->widthSized()) { | ||
| 648 | // See also error in V3Number | ||
| 649 | ✗ | nodeForUnsizedWarning(nodep)->v3warn( | |
| 650 | WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations."); | ||
| 651 | } | ||
| 652 | } | ||
| 653 | } | ||
| 654 | ✗ | void visit(AstConcatN* nodep) override { | |
| 655 | ✗ | if (nodep->didWidth()) return; | |
| 656 | // String concatenate. | ||
| 657 | // Already did AstConcat simplifications | ||
| 658 | ✗ | if (m_vup->prelim()) { | |
| 659 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 660 | ✗ | iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 661 | ✗ | nodep->dtypeSetString(); | |
| 662 | } | ||
| 663 | ✗ | if (m_vup->final()) { | |
| 664 | nodep->didWidth(true); | ||
| 665 | if (!nodep->dtypep()->widthSized()) { | ||
| 666 | // See also error in V3Number | ||
| 667 | ✗ | nodeForUnsizedWarning(nodep)->v3warn( | |
| 668 | WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations."); | ||
| 669 | } | ||
| 670 | } | ||
| 671 | } | ||
| 672 | ✗ | void visit(AstDefaultDisable* nodep) override { | |
| 673 | ✗ | assertAtStatement(nodep); | |
| 674 | // it's like an if() condition. | ||
| 675 | ✗ | iterateCheckBool(nodep, "default disable iff condition", nodep->condp(), BOTH); | |
| 676 | } | ||
| 677 | ✗ | void visit(AstDelay* nodep) override { | |
| 678 | ✗ | if (VN_IS(m_procedurep, Final)) { | |
| 679 | ✗ | nodep->v3error("Delays are not legal in final blocks (IEEE 1800-2023 9.2.3)"); | |
| 680 | VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
| 681 | ✗ | return; | |
| 682 | } | ||
| 683 | ✗ | if (VN_IS(m_ftaskp, Func)) { | |
| 684 | ✗ | nodep->v3error("Delays are not legal in functions. Suggest use a task " | |
| 685 | "(IEEE 1800-2023 13.4.4)"); | ||
| 686 | VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
| 687 | ✗ | return; | |
| 688 | } | ||
| 689 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 690 | ✗ | if (v3Global.opt.timing().isSetTrue()) { | |
| 691 | ✗ | iterateCheckDelay(nodep, "delay", nodep->lhsp(), BOTH); | |
| 692 | ✗ | iterateAndNextNull(nodep->stmtsp()); | |
| 693 | ✗ | return; | |
| 694 | ✗ | } else if (v3Global.opt.timing().isSetFalse()) { | |
| 695 | ✗ | nodep->v3warn(STMTDLY, "Ignoring delay on this statement due to --no-timing"); | |
| 696 | } else { | ||
| 697 | ✗ | nodep->v3warn( | |
| 698 | E_NEEDTIMINGOPT, | ||
| 699 | "Use --timing or --no-timing to specify how delays should be handled"); | ||
| 700 | } | ||
| 701 | } | ||
| 702 | ✗ | if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext()); | |
| 703 | VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
| 704 | } | ||
| 705 | ✗ | void visit(AstFork* nodep) override { | |
| 706 | ✗ | if (VN_IS(m_ftaskp, Func) && !nodep->joinType().joinNone()) { | |
| 707 | ✗ | nodep->v3error("Only fork .. join_none is legal in functions. " | |
| 708 | "(IEEE 1800-2023 13.4.4)"); | ||
| 709 | VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
| 710 | ✗ | return; | |
| 711 | } | ||
| 712 | if (!nodep->fileline()->timingOn() | ||
| 713 | // With no statements, begin is identical | ||
| 714 | ✗ | || !nodep->stmtsp() | |
| 715 | ✗ | || (!v3Global.opt.timing().isSetTrue() // If no --timing | |
| 716 | ✗ | && (v3Global.opt.bboxUnsup() | |
| 717 | // With one statement and no timing, a begin block does as good as a | ||
| 718 | // fork/join or join_any | ||
| 719 | ✗ | || (!nodep->stmtsp()->nextp() && !nodep->joinType().joinNone())))) { | |
| 720 | AstNode* stmtsp = nullptr; | ||
| 721 | ✗ | if (nodep->stmtsp()) stmtsp = nodep->stmtsp()->unlinkFrBack(); | |
| 722 | ✗ | AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), stmtsp}; | |
| 723 | ✗ | nodep->replaceWith(newp); | |
| 724 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 725 | ✗ | } else if (v3Global.opt.timing().isSetTrue()) { | |
| 726 | ✗ | iterateChildren(nodep); | |
| 727 | ✗ | } else if (v3Global.opt.timing().isSetFalse()) { | |
| 728 | ✗ | nodep->v3warn(E_NOTIMING, "Fork statements require --timing"); | |
| 729 | VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
| 730 | } else { | ||
| 731 | ✗ | nodep->v3warn(E_NEEDTIMINGOPT, | |
| 732 | "Use --timing or --no-timing to specify how forks should be handled"); | ||
| 733 | } | ||
| 734 | } | ||
| 735 | ✗ | void visit(AstDisableFork* nodep) override { | |
| 736 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 737 | ✗ | if (v3Global.opt.timing().isSetFalse()) { | |
| 738 | ✗ | nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing"); | |
| 739 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 740 | ✗ | } else if (!v3Global.opt.timing().isSetTrue()) { | |
| 741 | ✗ | nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how " | |
| 742 | << "disable fork should be handled"); | ||
| 743 | } | ||
| 744 | } | ||
| 745 | } | ||
| 746 | ✗ | void visit(AstWaitFork* nodep) override { | |
| 747 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 748 | ✗ | if (v3Global.opt.timing().isSetFalse()) { | |
| 749 | ✗ | nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing"); | |
| 750 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 751 | ✗ | } else if (!v3Global.opt.timing().isSetTrue()) { | |
| 752 | ✗ | nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how " | |
| 753 | << "disable fork should be handled"); | ||
| 754 | } | ||
| 755 | } | ||
| 756 | } | ||
| 757 | ✗ | void visit(AstToLowerN* nodep) override { | |
| 758 | ✗ | if (m_vup->prelim()) { | |
| 759 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 760 | ✗ | nodep->dtypeSetString(); | |
| 761 | } | ||
| 762 | } | ||
| 763 | ✗ | void visit(AstToUpperN* nodep) override { | |
| 764 | ✗ | if (m_vup->prelim()) { | |
| 765 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 766 | ✗ | nodep->dtypeSetString(); | |
| 767 | } | ||
| 768 | } | ||
| 769 | 106538 | void visit(AstReplicate* nodep) override { | |
| 770 | // IEEE-2012 Table 11-21: | ||
| 771 | // LHS, RHS is self-determined | ||
| 772 | // width: value(LHS) * width(RHS) | ||
| 773 |
2/2✓ Branch 0 taken 57261 times.
✓ Branch 1 taken 49277 times.
|
106538 | if (m_vup->prelim()) { |
| 774 | 57261 | iterateCheckSizedSelf(nodep, "RHS", nodep->countp(), SELF, BOTH); | |
| 775 | V3Const::constifyParamsNoWarnEdit(nodep->countp()); // rhsp may change | ||
| 776 | |||
| 777 | uint32_t times = 1; | ||
| 778 | |||
| 779 | const AstConst* const constp = VN_CAST(nodep->countp(), Const); | ||
| 780 | if (constp) times = constp->toUInt(); | ||
| 781 | |||
| 782 |
2/2✓ Branch 0 taken 12091 times.
✓ Branch 1 taken 45170 times.
|
57261 | AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp(); |
| 783 | if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType) | ||
| 784 | || VN_IS(vdtypep, UnpackArrayDType)) { | ||
| 785 | ✗ | if (times != 1) { | |
| 786 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-1 replication to form " | |
| 787 | << vdtypep->prettyDTypeNameQ() | ||
| 788 | << " data type"); | ||
| 789 | } | ||
| 790 | if (VN_IS(nodep->srcp(), Concat)) { | ||
| 791 | // Convert to concat directly, and visit(AstConst) will convert. | ||
| 792 | // Don't iterate lhsp as SELF, the potential Concat below needs | ||
| 793 | // the adtypep passed down to recognize the QueueDType | ||
| 794 | ✗ | userIterateAndNext(nodep->srcp(), WidthVP{vdtypep, BOTH}.p()); | |
| 795 | ✗ | nodep->replaceWith(nodep->srcp()->unlinkFrBack()); | |
| 796 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 797 | ✗ | return; | |
| 798 | } else { // int a[] = {lhs} -> same as '{lhs} | ||
| 799 | auto* const newp = new AstPattern{ | ||
| 800 | nodep->fileline(), | ||
| 801 | new AstPatMember{nodep->srcp()->fileline(), nodep->srcp()->unlinkFrBack(), | ||
| 802 | ✗ | nullptr, nullptr}}; | |
| 803 | ✗ | nodep->replaceWith(newp); | |
| 804 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 805 | ✗ | userIterate(newp, m_vup); | |
| 806 | ✗ | return; | |
| 807 | } | ||
| 808 | } | ||
| 809 | if (VN_IS(vdtypep, AssocArrayDType)) { | ||
| 810 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: Replication to form " | |
| 811 | << vdtypep->prettyDTypeNameQ() << " data type"); | ||
| 812 | } | ||
| 813 |
3/4✓ Branch 0 taken 12091 times.
✓ Branch 1 taken 45170 times.
✓ Branch 3 taken 12091 times.
✗ Branch 4 not taken.
|
57261 | if (vdtypep && vdtypep->isString()) { |
| 814 | ✗ | iterateCheckString(nodep, "LHS", nodep->srcp(), BOTH); | |
| 815 | } else { | ||
| 816 | 57261 | iterateCheckSizedSelf(nodep, "LHS", nodep->srcp(), SELF, BOTH); | |
| 817 | } | ||
| 818 | |||
| 819 |
4/6✓ Branch 0 taken 12091 times.
✓ Branch 1 taken 45170 times.
✓ Branch 3 taken 12091 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 57261 times.
✗ Branch 7 not taken.
|
57261 | if ((vdtypep && vdtypep->isString()) || nodep->srcp()->isString()) { |
| 820 | AstNode* const newp | ||
| 821 | = new AstReplicateN{nodep->fileline(), nodep->srcp()->unlinkFrBack(), | ||
| 822 | ✗ | nodep->countp()->unlinkFrBack()}; | |
| 823 | ✗ | nodep->replaceWith(newp); | |
| 824 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 825 | ✗ | return; | |
| 826 | } else { | ||
| 827 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57261 times.
|
57261 | if (!constp) nodep->v3error("Replication value isn't a constant."); |
| 828 | if (times == 0 | ||
| 829 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57261 times.
|
57261 | && !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up. |
| 830 | ✗ | nodep->v3warn(ZEROREPL, | |
| 831 | "Replication value of 0 is only legal under a concatenation" | ||
| 832 | " (IEEE 1800-2023 11.4.12.1)"); | ||
| 833 | times = 1; // Set to 1, so we can continue looking for errors | ||
| 834 | } | ||
| 835 |
1/2✓ Branch 0 taken 57261 times.
✗ Branch 1 not taken.
|
171783 | nodep->dtypeSetLogicUnsized((nodep->srcp()->width() * times), |
| 836 |
1/2✓ Branch 0 taken 57261 times.
✗ Branch 1 not taken.
|
57261 | (nodep->srcp()->widthMin() * times), |
| 837 | VSigning::UNSIGNED); | ||
| 838 | } | ||
| 839 | } | ||
| 840 |
2/2✓ Branch 0 taken 57261 times.
✓ Branch 1 taken 49277 times.
|
106538 | if (m_vup->final()) { |
| 841 | if (!nodep->dtypep()->widthSized()) { | ||
| 842 | // See also error in V3Number | ||
| 843 | ✗ | nodeForUnsizedWarning(nodep)->v3warn( | |
| 844 | WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications."); | ||
| 845 | } | ||
| 846 | } | ||
| 847 | } | ||
| 848 | ✗ | void visit(AstReplicateN* nodep) override { | |
| 849 | // Replicate with string | ||
| 850 | ✗ | if (m_vup->prelim()) { | |
| 851 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 852 | ✗ | iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); | |
| 853 | V3Const::constifyParamsNoWarnEdit(nodep->rhsp()); // rhsp may change | ||
| 854 | ✗ | nodep->dtypeSetString(); | |
| 855 | } | ||
| 856 | ✗ | if (m_vup->final()) { | |
| 857 | if (!nodep->dtypep()->widthSized()) { | ||
| 858 | // See also error in V3Number | ||
| 859 | ✗ | nodeForUnsizedWarning(nodep)->v3warn( | |
| 860 | WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications."); | ||
| 861 | } | ||
| 862 | } | ||
| 863 | } | ||
| 864 | ✗ | void visit(AstNodeDistBiop* nodep) override { | |
| 865 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 866 | ✗ | iterateCheckSigned32(nodep, "seed", nodep->lhsp(), BOTH); | |
| 867 | ✗ | iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 868 | ✗ | nodep->dtypeSetSigned32(); | |
| 869 | } | ||
| 870 | } | ||
| 871 | ✗ | void visit(AstNodeDistTriop* nodep) override { | |
| 872 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 873 | ✗ | iterateCheckSigned32(nodep, "seed", nodep->lhsp(), BOTH); | |
| 874 | ✗ | iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 875 | ✗ | iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH); | |
| 876 | ✗ | nodep->dtypeSetSigned32(); | |
| 877 | } | ||
| 878 | } | ||
| 879 | ✗ | void visit(AstNodeStream* nodep) override { | |
| 880 | ✗ | if (m_vup->prelim()) { | |
| 881 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); | |
| 882 | ✗ | iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); | |
| 883 | V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change | ||
| 884 | if (const AstConst* const constp = VN_CAST(nodep->rhsp(), Const)) { | ||
| 885 | ✗ | if (constp->toUInt() == 0) nodep->v3error("Slice size cannot be zero."); | |
| 886 | } else { | ||
| 887 | ✗ | nodep->v3error("Slice size isn't a constant or basic data type."); | |
| 888 | } | ||
| 889 | const AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefToEnump(); | ||
| 890 | if (VN_IS(lhsDtypep, DynArrayDType) || VN_IS(lhsDtypep, QueueDType) | ||
| 891 | || VN_IS(lhsDtypep, UnpackArrayDType)) { | ||
| 892 | ✗ | nodep->dtypeSetStream(); | |
| 893 | ✗ | } else if (lhsDtypep->isCompound()) { | |
| 894 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 895 | "Unsupported: Stream operation on a variable of a type " | ||
| 896 | << lhsDtypep->prettyDTypeNameQ()); | ||
| 897 | nodep->dtypeSetStream(); | ||
| 898 | } else { | ||
| 899 | ✗ | nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(), | |
| 900 | VSigning::UNSIGNED); | ||
| 901 | } | ||
| 902 | } | ||
| 903 | ✗ | if (m_vup->final()) { | |
| 904 | if (!nodep->dtypep()->widthSized()) { | ||
| 905 | // See also error in V3Number | ||
| 906 | ✗ | nodeForUnsizedWarning(nodep)->v3warn( | |
| 907 | WIDTHCONCAT, "Unsized numbers/parameters not allowed in streams."); | ||
| 908 | } | ||
| 909 | } | ||
| 910 | } | ||
| 911 | 77784 | void visit(AstRange* nodep) override { | |
| 912 | // Real: Not allowed | ||
| 913 | // Signed: unsigned output, input either | ||
| 914 | // Convert all range values to constants | ||
| 915 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 77784 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
77784 | UINFO(6, "RANGE " << nodep << endl); |
| 916 | V3Const::constifyParamsEdit(nodep->leftp()); // May relink pointed to node | ||
| 917 | V3Const::constifyParamsEdit(nodep->rightp()); // May relink pointed to node | ||
| 918 |
1/2✓ Branch 2 taken 77784 times.
✗ Branch 3 not taken.
|
77784 | checkConstantOrReplace(nodep->leftp(), "left side of bit range isn't a constant"); |
| 919 |
1/2✓ Branch 2 taken 77784 times.
✗ Branch 3 not taken.
|
77784 | checkConstantOrReplace(nodep->rightp(), "right side of bit range isn't a constant"); |
| 920 |
1/2✓ Branch 0 taken 77784 times.
✗ Branch 1 not taken.
|
77784 | if (m_vup->prelim()) { |
| 921 | // Don't need to iterate because V3Const already constified | ||
| 922 | 77784 | const int width = nodep->elementsConst(); | |
| 923 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 77784 times.
|
77784 | if (width > (1 << 28)) { |
| 924 | ✗ | nodep->v3error("Width of bit range is huge; vector of over 1 billion bits: 0x" | |
| 925 | << std::hex << width << std::dec); | ||
| 926 | } | ||
| 927 | // Note width() not set on range; use elementsConst() | ||
| 928 | 77784 | if (nodep->ascending() && !VN_IS(nodep->backp(), UnpackArrayDType) | |
| 929 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 77784 times.
|
77784 | && !VN_IS(nodep->backp(), Cell)) { // For cells we warn in V3Inst |
| 930 | ✗ | nodep->v3warn(ASCRANGE, "Ascending bit range vector: left < right of bit range: [" | |
| 931 | << nodep->leftConst() << ":" << nodep->rightConst() | ||
| 932 | << "]"); | ||
| 933 | } | ||
| 934 | } | ||
| 935 | 77784 | } | |
| 936 | |||
| 937 |
1/2✓ Branch 0 taken 203939 times.
✗ Branch 1 not taken.
|
203939 | void visit(AstSel* nodep) override { |
| 938 | // Signed: always unsigned; Real: Not allowed | ||
| 939 | // LSB is self-determined (IEEE 2012 11.5.1) | ||
| 940 | // We also use SELs to shorten a signed constant etc, in this case they are signed. | ||
| 941 |
1/2✓ Branch 0 taken 203939 times.
✗ Branch 1 not taken.
|
203939 | if (nodep->didWidth()) return; |
| 942 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 203939 times.
|
203939 | UASSERT_OBJ(m_vup, nodep, "Select under an unexpected context"); |
| 943 |
2/2✓ Branch 0 taken 135230 times.
✓ Branch 1 taken 68709 times.
|
203939 | if (m_vup->prelim()) { |
| 944 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 135230 times.
|
135230 | if (debug() >= 9) nodep->dumpTree("- selWidth: "); |
| 945 | 135230 | userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 946 | 135230 | userIterateAndNext(nodep->lsbp(), WidthVP{SELF, PRELIM}.p()); | |
| 947 | 135230 | checkCvtUS(nodep->fromp()); | |
| 948 | 135230 | iterateCheckSizedSelf(nodep, "Select Width", nodep->widthp(), SELF, BOTH); | |
| 949 | 135230 | iterateCheckSizedSelf(nodep, "Select LHS", nodep->fromp(), SELF, BOTH); | |
| 950 | V3Const::constifyParamsEdit(nodep->widthp()); // widthp may change | ||
| 951 | const AstConst* const widthConstp = VN_CAST(nodep->widthp(), Const); | ||
| 952 | if (!widthConstp) { | ||
| 953 | ✗ | nodep->v3error( | |
| 954 | "Width of bit extract isn't a constant"); // Impossible? // LCOV_EXCL_LINE | ||
| 955 | ✗ | nodep->dtypeSetBit(); | |
| 956 | ✗ | return; | |
| 957 | } | ||
| 958 | int width = nodep->widthConst(); | ||
| 959 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 135230 times.
|
135230 | if (width <= 0) { |
| 960 | ✗ | nodep->v3error("Width of bit extract must be positive (IEEE 1800-2023 11.5.1)"); | |
| 961 | ✗ | nodep->dtypeSetBit(); | |
| 962 | ✗ | return; | |
| 963 | } | ||
| 964 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 135230 times.
|
135230 | UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set"); // by V3WidthSel |
| 965 |
1/2✓ Branch 1 taken 100397 times.
✗ Branch 2 not taken.
|
200794 | if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() < nodep->lsbConst()) { |
| 966 | // Likely impossible given above width check | ||
| 967 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 968 | "Unsupported: left < right of bit extract: " // LCOV_EXCL_LINE | ||
| 969 | << nodep->msbConst() << "<" << nodep->lsbConst()); | ||
| 970 | ✗ | width = (nodep->lsbConst() - nodep->msbConst() + 1); | |
| 971 | ✗ | nodep->dtypeSetLogicSized(width, VSigning::UNSIGNED); | |
| 972 | pushDeletep(nodep->widthp()); | ||
| 973 | ✗ | nodep->widthp()->replaceWith(new AstConst(nodep->widthp()->fileline(), width)); | |
| 974 | pushDeletep(nodep->lsbp()); | ||
| 975 | ✗ | nodep->lsbp()->replaceWith(new AstConst{nodep->lsbp()->fileline(), 0}); | |
| 976 | } | ||
| 977 | // We're extracting, so just make sure the expression is at least wide enough. | ||
| 978 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 135230 times.
|
135230 | if (nodep->fromp()->width() < width) { |
| 979 | ✗ | nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only " | |
| 980 | << nodep->fromp()->width() << " bit number"); | ||
| 981 | // Extend it. | ||
| 982 | AstNodeDType* const subDTypep | ||
| 983 | ✗ | = nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric()); | |
| 984 | ✗ | widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP, | |
| 985 | false /*noerror*/); | ||
| 986 | } | ||
| 987 | // Check bit indexes. | ||
| 988 | // What is the MSB? We want the true MSB, not one starting at | ||
| 989 | // 0, because a 4 bit index is required to look at a one-bit | ||
| 990 | // variable[15:15] and 5 bits for [15:-2] | ||
| 991 |
1/2✓ Branch 0 taken 135230 times.
✗ Branch 1 not taken.
|
135230 | int frommsb = nodep->fromp()->width() - 1; |
| 992 | int fromlsb = 0; | ||
| 993 | const int elw = nodep->declElWidth(); // Must adjust to tell user bit ranges | ||
| 994 |
1/2✓ Branch 0 taken 135230 times.
✗ Branch 1 not taken.
|
135230 | if (nodep->declRange().ranged()) { |
| 995 | 135230 | frommsb = nodep->declRange().hiMaxSelect() * elw | |
| 996 | 135230 | + (elw - 1); // Corrected for negative lsb | |
| 997 | 135230 | fromlsb = nodep->declRange().lo() * elw; | |
| 998 | } else { | ||
| 999 | // nodep->v3fatalSrc("Should have been declRanged in V3WidthSel"); | ||
| 1000 | } | ||
| 1001 | 135230 | const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1; // Width to address a bit | |
| 1002 | AstNodeDType* const selwidthDTypep | ||
| 1003 | 135230 | = nodep->findLogicDType(selwidth, selwidth, nodep->lsbp()->dtypep()->numeric()); | |
| 1004 | 135230 | userIterateAndNext(nodep->fromp(), WidthVP{SELF, FINAL}.p()); | |
| 1005 | 135230 | userIterateAndNext(nodep->lsbp(), WidthVP{SELF, FINAL}.p()); | |
| 1006 |
3/4✓ Branch 1 taken 73528 times.
✓ Branch 2 taken 61702 times.
✓ Branch 3 taken 73528 times.
✗ Branch 4 not taken.
|
208758 | if (widthBad(nodep->lsbp(), selwidthDTypep) && nodep->lsbp()->width() != 32) { |
| 1007 |
1/2✓ Branch 1 taken 73528 times.
✗ Branch 2 not taken.
|
73528 | if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) { |
| 1008 |
7/18✓ Branch 0 taken 73528 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 73528 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 73528 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 73528 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 73528 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 73528 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✓ Branch 25 taken 73528 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
|
588224 | nodep->v3widthWarn( |
| 1009 | (selwidth / elw), (nodep->lsbp()->width() / elw), | ||
| 1010 | "Bit extraction of var[" | ||
| 1011 | << (frommsb / elw) << ":" << (fromlsb / elw) << "] requires " | ||
| 1012 | << (selwidth / elw) << " bit index, not " | ||
| 1013 | << (nodep->lsbp()->width() / elw) | ||
| 1014 | << (nodep->lsbp()->width() != nodep->lsbp()->widthMin() | ||
| 1015 | ? " or " + cvtToStr(nodep->lsbp()->widthMin() / elw) | ||
| 1016 | : "") | ||
| 1017 | << " bits."); | ||
| 1018 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 73528 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
73528 | UINFO(1, " Related node: " << nodep << endl); |
| 1019 | } | ||
| 1020 | } | ||
| 1021 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 100397 times.
|
100397 | if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() > frommsb) { |
| 1022 | // See also warning in V3Const | ||
| 1023 | // We need to check here, because the widthCheckSized may silently | ||
| 1024 | // add another SEL which will lose the out-of-range check | ||
| 1025 | // | ||
| 1026 | // We don't want to trigger an error here if we are just | ||
| 1027 | // evaluating type sizes for a generate block condition. We | ||
| 1028 | // should only trigger the error if the out-of-range access is | ||
| 1029 | // actually generated. | ||
| 1030 | ✗ | if (m_doGenerate) { | |
| 1031 | ✗ | UINFO(5, "Selection index out of range inside generate." << endl); | |
| 1032 | } else { | ||
| 1033 | ✗ | nodep->v3warn(SELRANGE, "Selection index out of range: " | |
| 1034 | << nodep->msbConst() << ":" << nodep->lsbConst() | ||
| 1035 | << " outside " << frommsb << ":" << fromlsb); | ||
| 1036 | ✗ | UINFO(1, " Related node: " << nodep << endl); | |
| 1037 | } | ||
| 1038 | // Extend it. | ||
| 1039 | ✗ | const int extendTo = nodep->msbConst() + 1; | |
| 1040 | ✗ | AstNodeDType* const subDTypep = nodep->findLogicDType( | |
| 1041 | extendTo, extendTo, nodep->fromp()->dtypep()->numeric()); | ||
| 1042 | ✗ | widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP, | |
| 1043 | false /*noerror*/); | ||
| 1044 | } | ||
| 1045 | // iterate FINAL is two blocks above | ||
| 1046 | // | ||
| 1047 | // If we have a width problem with GENERATE etc, this will reduce | ||
| 1048 | // it down and mask it, so we have no chance of finding a real | ||
| 1049 | // error in the future. So don't do this for them. | ||
| 1050 |
1/2✓ Branch 0 taken 135230 times.
✗ Branch 1 not taken.
|
135230 | if (!m_doGenerate) { |
| 1051 | // lsbp() must be self-determined, however for performance | ||
| 1052 | // we want the select to be truncated to fit within the | ||
| 1053 | // maximum select range, e.g. turn Xs outside of the select | ||
| 1054 | // into something fast which pulls from within the array. | ||
| 1055 | 135230 | widthCheckSized(nodep, "Extract Range", nodep->lsbp(), selwidthDTypep, EXTEND_EXP, | |
| 1056 | false /*NOWARN*/); | ||
| 1057 | } | ||
| 1058 | } | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | ✗ | void visit(AstArraySel* nodep) override { | |
| 1062 | // Signed/Real: Output signed iff LHS signed/real; binary operator | ||
| 1063 | // Note by contrast, bit extract selects are unsigned | ||
| 1064 | // LSB is self-determined (IEEE 2012 11.5.1) | ||
| 1065 | ✗ | if (m_vup->prelim()) { | |
| 1066 | ✗ | iterateCheckSizedSelf(nodep, "Bit select", nodep->bitp(), SELF, BOTH); | |
| 1067 | ✗ | userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); | |
| 1068 | // | ||
| 1069 | int frommsb; | ||
| 1070 | int fromlsb; | ||
| 1071 | const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); | ||
| 1072 | if (const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { | ||
| 1073 | frommsb = adtypep->hi(); | ||
| 1074 | fromlsb = adtypep->lo(); | ||
| 1075 | ✗ | if (fromlsb > frommsb) { | |
| 1076 | const int t = frommsb; | ||
| 1077 | frommsb = fromlsb; | ||
| 1078 | fromlsb = t; | ||
| 1079 | } | ||
| 1080 | // However, if the lsb<0 we may go negative, so need more bits! | ||
| 1081 | ✗ | if (fromlsb < 0) frommsb += -fromlsb; | |
| 1082 | ✗ | nodep->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference | |
| 1083 | } else { | ||
| 1084 | // Note PackArrayDType doesn't use an ArraySel but a normal Sel. | ||
| 1085 | ✗ | UINFO(1, " Related dtype: " << fromDtp << endl); | |
| 1086 | ✗ | nodep->v3fatalSrc("Array reference exceeds dimension of array"); | |
| 1087 | frommsb = fromlsb = 0; | ||
| 1088 | } | ||
| 1089 | ✗ | const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1; // Width to address a bit | |
| 1090 | AstNodeDType* const selwidthDTypep | ||
| 1091 | ✗ | = nodep->findLogicDType(selwidth, selwidth, nodep->bitp()->dtypep()->numeric()); | |
| 1092 | ✗ | if (widthBad(nodep->bitp(), selwidthDTypep) && nodep->bitp()->width() != 32) { | |
| 1093 | ✗ | nodep->v3widthWarn(selwidth, nodep->bitp()->width(), | |
| 1094 | "Bit extraction of array[" | ||
| 1095 | << frommsb << ":" << fromlsb << "] requires " << selwidth | ||
| 1096 | << " bit index, not " << nodep->bitp()->width() | ||
| 1097 | << (nodep->bitp()->width() != nodep->bitp()->widthMin() | ||
| 1098 | ? " or " + cvtToStr(nodep->bitp()->widthMin()) | ||
| 1099 | : "") | ||
| 1100 | << " bits."); | ||
| 1101 | ✗ | if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) { | |
| 1102 | ✗ | UINFO(1, " Related node: " << nodep << endl); | |
| 1103 | ✗ | UINFO(1, " Related dtype: " << nodep->dtypep() << endl); | |
| 1104 | } | ||
| 1105 | } | ||
| 1106 | ✗ | if (!m_doGenerate) { | |
| 1107 | // Must check bounds before adding a select that truncates the bound | ||
| 1108 | // Note we've already subtracted off LSB | ||
| 1109 | if (VN_IS(nodep->bitp(), Const) | ||
| 1110 | ✗ | && (VN_AS(nodep->bitp(), Const)->toSInt() > (frommsb - fromlsb) | |
| 1111 | ✗ | || VN_AS(nodep->bitp(), Const)->toSInt() < 0)) { | |
| 1112 | ✗ | nodep->v3warn(SELRANGE, | |
| 1113 | "Selection index out of range: " | ||
| 1114 | << (VN_AS(nodep->bitp(), Const)->toSInt() + fromlsb) | ||
| 1115 | << " outside " << frommsb << ":" << fromlsb); | ||
| 1116 | ✗ | UINFO(1, " Related node: " << nodep << endl); | |
| 1117 | } | ||
| 1118 | ✗ | widthCheckSized(nodep, "Extract Range", nodep->bitp(), selwidthDTypep, EXTEND_EXP, | |
| 1119 | false /*NOWARN*/); | ||
| 1120 | } | ||
| 1121 | } | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | ✗ | void visit(AstAssocSel* nodep) override { | |
| 1125 | // Signed/Real: Output type based on array-declared type; binary operator | ||
| 1126 | ✗ | if (m_vup->prelim()) { | |
| 1127 | const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); | ||
| 1128 | const AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType); | ||
| 1129 | if (!adtypep) { | ||
| 1130 | ✗ | UINFO(1, " Related dtype: " << fromDtp << endl); | |
| 1131 | ✗ | nodep->v3fatalSrc("Associative array reference is not to associative array"); | |
| 1132 | } | ||
| 1133 | ✗ | iterateCheckTyped(nodep, "Associative select", nodep->bitp(), adtypep->keyDTypep(), | |
| 1134 | BOTH); | ||
| 1135 | nodep->dtypeFrom(adtypep->subDTypep()); | ||
| 1136 | } | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | ✗ | void visit(AstWildcardSel* nodep) override { | |
| 1140 | // Signed/Real: Output type based on array-declared type; binary operator | ||
| 1141 | ✗ | if (m_vup->prelim()) { | |
| 1142 | const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); | ||
| 1143 | const AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType); | ||
| 1144 | if (!adtypep) { | ||
| 1145 | ✗ | UINFO(1, " Related dtype: " << fromDtp << endl); | |
| 1146 | ✗ | nodep->v3fatalSrc("Wildcard array reference is not to wildcard array"); | |
| 1147 | } | ||
| 1148 | ✗ | const AstBasicDType* const basicp = nodep->bitp()->dtypep()->skipRefp()->basicp(); | |
| 1149 | if (!basicp | ||
| 1150 | ✗ | || (basicp->keyword() != VBasicDTypeKwd::STRING | |
| 1151 | && !basicp->keyword().isIntNumeric())) { | ||
| 1152 | ✗ | nodep->v3error("Wildcard index must be integral (IEEE 1800-2023 7.8.1)"); | |
| 1153 | } | ||
| 1154 | ✗ | iterateCheckTyped(nodep, "Wildcard associative select", nodep->bitp(), | |
| 1155 | adtypep->findStringDType(), BOTH); | ||
| 1156 | nodep->dtypeFrom(adtypep->subDTypep()); | ||
| 1157 | } | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | ✗ | void visit(AstSliceSel* nodep) override { | |
| 1161 | // Always creates as output an unpacked array | ||
| 1162 | ✗ | if (m_vup->prelim()) { | |
| 1163 | ✗ | userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); | |
| 1164 | // | ||
| 1165 | // Array indices are always constant | ||
| 1166 | const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); | ||
| 1167 | const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType); | ||
| 1168 | if (!adtypep) { | ||
| 1169 | ✗ | UINFO(1, " Related dtype: " << fromDtp << endl); | |
| 1170 | ✗ | nodep->v3fatalSrc("Packed array reference exceeds dimension of array"); | |
| 1171 | } | ||
| 1172 | // Build new array Dtype based on the original's base type, but with new bounds | ||
| 1173 | AstNodeDType* const newDtp | ||
| 1174 | = new AstUnpackArrayDType{nodep->fileline(), adtypep->subDTypep(), | ||
| 1175 | ✗ | new AstRange{nodep->fileline(), nodep->declRange()}}; | |
| 1176 | v3Global.rootp()->typeTablep()->addTypesp(newDtp); | ||
| 1177 | nodep->dtypeFrom(newDtp); | ||
| 1178 | |||
| 1179 | ✗ | if (!m_doGenerate) { | |
| 1180 | // Must check bounds before adding a select that truncates the bound | ||
| 1181 | // Note we've already subtracted off LSB | ||
| 1182 | ✗ | const int subtracted = adtypep->declRange().lo(); | |
| 1183 | // Add subtracted value to get the original range | ||
| 1184 | const VNumRange declRange{nodep->declRange().hi() + subtracted, | ||
| 1185 | nodep->declRange().lo() + subtracted, | ||
| 1186 | ✗ | nodep->declRange().ascending()}; | |
| 1187 | ✗ | if ((declRange.hi() > adtypep->declRange().hi()) | |
| 1188 | ✗ | || declRange.lo() < adtypep->declRange().lo()) { | |
| 1189 | // Other simulators warn too | ||
| 1190 | ✗ | nodep->v3error("Slice selection index '" << declRange << "'" | |
| 1191 | << " outside data type's '" | ||
| 1192 | << adtypep->declRange() << "'"); | ||
| 1193 | ✗ | } else if ((declRange.ascending() != adtypep->declRange().ascending()) | |
| 1194 | ✗ | && declRange.hi() != declRange.lo()) { | |
| 1195 | ✗ | nodep->v3error("Slice selection '" | |
| 1196 | << declRange << "'" | ||
| 1197 | << " has reversed range order versus data type's '" | ||
| 1198 | << adtypep->declRange() << "'"); | ||
| 1199 | } | ||
| 1200 | } | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | ✗ | void visit(AstSelBit* nodep) override { | |
| 1205 | // Just a quick check as after V3Param these nodes instead are AstSel's | ||
| 1206 | ✗ | userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1207 | ✗ | userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1208 | ✗ | userIterateAndNext(nodep->thsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1209 | ✗ | userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p()); | |
| 1210 | ✗ | AstNode* const selp = V3Width::widthSelNoIterEdit(nodep); | |
| 1211 | ✗ | if (selp != nodep) { | |
| 1212 | VL_DANGLING(nodep); | ||
| 1213 | ✗ | userIterate(selp, m_vup); | |
| 1214 | ✗ | return; | |
| 1215 | } | ||
| 1216 | ✗ | nodep->v3fatalSrc("AstSelBit should disappear after widthSel"); | |
| 1217 | } | ||
| 1218 | 79812 | void visit(AstSelExtract* nodep) override { | |
| 1219 | // Just a quick check as after V3Param these nodes instead are AstSel's | ||
| 1220 | 79812 | userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1221 | 79812 | userIterateAndNext(nodep->leftp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1222 | 79812 | userIterateAndNext(nodep->rightp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1223 | 79812 | userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p()); | |
| 1224 | 79812 | AstNode* const selp = V3Width::widthSelNoIterEdit(nodep); | |
| 1225 |
1/2✓ Branch 0 taken 79812 times.
✗ Branch 1 not taken.
|
79812 | if (selp != nodep) { |
| 1226 | nodep = nullptr; | ||
| 1227 | 79812 | userIterate(selp, m_vup); | |
| 1228 | 79812 | return; | |
| 1229 | } | ||
| 1230 | ✗ | nodep->v3fatalSrc("AstSelExtract should disappear after widthSel"); | |
| 1231 | } | ||
| 1232 | ✗ | void visit(AstSelPlus* nodep) override { | |
| 1233 | ✗ | userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1234 | ✗ | userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1235 | ✗ | userIterateAndNext(nodep->widthp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1236 | ✗ | userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p()); | |
| 1237 | ✗ | AstNode* const selp = V3Width::widthSelNoIterEdit(nodep); | |
| 1238 | ✗ | if (selp != nodep) { | |
| 1239 | nodep = nullptr; | ||
| 1240 | ✗ | userIterate(selp, m_vup); | |
| 1241 | ✗ | return; | |
| 1242 | } | ||
| 1243 | ✗ | nodep->v3fatalSrc("AstSelPlus should disappear after widthSel"); | |
| 1244 | } | ||
| 1245 | ✗ | void visit(AstSelMinus* nodep) override { | |
| 1246 | ✗ | userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1247 | ✗ | userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1248 | ✗ | userIterateAndNext(nodep->widthp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel | |
| 1249 | ✗ | userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p()); | |
| 1250 | ✗ | AstNode* const selp = V3Width::widthSelNoIterEdit(nodep); | |
| 1251 | ✗ | if (selp != nodep) { | |
| 1252 | nodep = nullptr; | ||
| 1253 | ✗ | userIterate(selp, m_vup); | |
| 1254 | ✗ | return; | |
| 1255 | } | ||
| 1256 | ✗ | nodep->v3fatalSrc("AstSelMinus should disappear after widthSel"); | |
| 1257 | } | ||
| 1258 | |||
| 1259 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
|
83 | void visit(AstExtend* nodep) override { |
| 1260 | // Typically created by this process, so we know width from here down is correct. | ||
| 1261 | // Exception is extend added by V3WidthSel - those need iteration | ||
| 1262 | if (nodep->didWidthAndSet()) return; | ||
| 1263 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1264 | } | ||
| 1265 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 882 times.
|
882 | void visit(AstExtendS* nodep) override { |
| 1266 | // Typically created by this process, so we know width from here down is correct. | ||
| 1267 | // Exception is extend added by V3WidthSel - those need iteration | ||
| 1268 | if (nodep->didWidthAndSet()) return; | ||
| 1269 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1270 | } | ||
| 1271 | 1297795 | void visit(AstConst* nodep) override { | |
| 1272 | // The node got setup with the signed/real state of the node. | ||
| 1273 | // However a later operation may have changed the node->signed w/o changing | ||
| 1274 | // the number's sign. So we don't: nodep->dtypeChgSigned(nodep->num().isSigned()); | ||
| 1275 |
2/2✓ Branch 0 taken 589705 times.
✓ Branch 1 taken 708090 times.
|
1297795 | if (nodep->didWidthAndSet()) return; |
| 1276 |
2/4✓ Branch 0 taken 589705 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 589705 times.
✗ Branch 3 not taken.
|
589705 | if (m_vup && m_vup->prelim()) { |
| 1277 | if (VN_IS(nodep->dtypep()->skipRefToEnump(), EnumDType)) { | ||
| 1278 | // Assume this constant was properly casted earlier | ||
| 1279 | // (Otherwise it couldn't have an enum data type) | ||
| 1280 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 589705 times.
|
589705 | } else if (nodep->num().isString()) { |
| 1281 | ✗ | nodep->dtypeSetString(); | |
| 1282 |
2/2✓ Branch 0 taken 509893 times.
✓ Branch 1 taken 79812 times.
|
589705 | } else if (nodep->num().sized()) { |
| 1283 | 509893 | nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width()); | |
| 1284 | } else { | ||
| 1285 | 79812 | nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthMin()); | |
| 1286 | } | ||
| 1287 | } | ||
| 1288 | // We don't size the constant until we commit the widths, as need parameters | ||
| 1289 | // to remain unsized, and numbers to remain unsized to avoid backp() warnings | ||
| 1290 | } | ||
| 1291 | ✗ | void visit(AstEmptyQueue* nodep) override { | |
| 1292 | ✗ | nodep->dtypeSetEmptyQueue(); | |
| 1293 | if (!VN_IS(nodep->backp(), Assign) && !VN_IS(nodep->backp(), Var)) { | ||
| 1294 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 1295 | "Unsupported/Illegal: empty queue ('{}') in this context"); | ||
| 1296 | } | ||
| 1297 | } | ||
| 1298 | ✗ | void visit(AstFell* nodep) override { | |
| 1299 | ✗ | if (m_vup->prelim()) { | |
| 1300 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); | |
| 1301 | userIterate(nodep->sentreep(), nullptr); | ||
| 1302 | ✗ | nodep->dtypeSetBit(); | |
| 1303 | } | ||
| 1304 | } | ||
| 1305 | ✗ | void visit(AstPast* nodep) override { | |
| 1306 | ✗ | if (m_vup->prelim()) { | |
| 1307 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); | |
| 1308 | ✗ | nodep->dtypeFrom(nodep->exprp()); | |
| 1309 | ✗ | if (nodep->ticksp()) { | |
| 1310 | ✗ | iterateCheckSizedSelf(nodep, "Ticks", nodep->ticksp(), SELF, BOTH); | |
| 1311 | ✗ | V3Const::constifyParamsEdit(nodep->ticksp()); // ticksp may change | |
| 1312 | const AstConst* const constp = VN_CAST(nodep->ticksp(), Const); | ||
| 1313 | if (!constp) { | ||
| 1314 | ✗ | nodep->v3error("$past tick value must be constant (IEEE 1800-2023 16.9.3)"); | |
| 1315 | ✗ | nodep->ticksp()->unlinkFrBack()->deleteTree(); | |
| 1316 | ✗ | } else if (constp->toSInt() < 1) { | |
| 1317 | ✗ | constp->v3error("$past tick value must be >= 1 (IEEE 1800-2023 16.9.3)"); | |
| 1318 | ✗ | nodep->ticksp()->unlinkFrBack()->deleteTree(); | |
| 1319 | } else { | ||
| 1320 | ✗ | if (constp->toSInt() > 10) { | |
| 1321 | ✗ | constp->v3warn(TICKCOUNT, "$past tick value of " | |
| 1322 | << constp->toSInt() | ||
| 1323 | << " may have a large performance cost"); | ||
| 1324 | } | ||
| 1325 | } | ||
| 1326 | } | ||
| 1327 | } | ||
| 1328 | } | ||
| 1329 | ✗ | void visit(AstRose* nodep) override { | |
| 1330 | ✗ | if (m_vup->prelim()) { | |
| 1331 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); | |
| 1332 | userIterate(nodep->sentreep(), nullptr); | ||
| 1333 | ✗ | nodep->dtypeSetBit(); | |
| 1334 | } | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | ✗ | void visit(AstSampled* nodep) override { | |
| 1338 | ✗ | if (m_vup->prelim()) { | |
| 1339 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); | |
| 1340 | nodep->dtypeFrom(nodep->exprp()); | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | ✗ | void visit(AstStable* nodep) override { | |
| 1345 | ✗ | if (m_vup->prelim()) { | |
| 1346 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); | |
| 1347 | userIterate(nodep->sentreep(), nullptr); | ||
| 1348 | ✗ | nodep->dtypeSetBit(); | |
| 1349 | } | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | ✗ | void visit(AstImplication* nodep) override { | |
| 1353 | ✗ | if (m_vup->prelim()) { | |
| 1354 | ✗ | iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 1355 | ✗ | iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 1356 | ✗ | nodep->dtypeSetBit(); | |
| 1357 | } | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | ✗ | void visit(AstRand* nodep) override { | |
| 1361 | ✗ | if (m_vup->prelim()) { | |
| 1362 | ✗ | if (nodep->urandom()) { | |
| 1363 | ✗ | nodep->dtypeSetUInt32(); // Says the spec | |
| 1364 | } else { | ||
| 1365 | ✗ | nodep->dtypeSetSigned32(); // Says the spec | |
| 1366 | } | ||
| 1367 | ✗ | if (nodep->seedp()) iterateCheckSigned32(nodep, "seed", nodep->seedp(), BOTH); | |
| 1368 | } | ||
| 1369 | } | ||
| 1370 | ✗ | void visit(AstURandomRange* nodep) override { | |
| 1371 | ✗ | if (m_vup->prelim()) { | |
| 1372 | ✗ | nodep->dtypeSetUInt32(); // Says the spec | |
| 1373 | AstNodeDType* const expDTypep = nodep->findUInt32DType(); | ||
| 1374 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 1375 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 1376 | ✗ | iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 1377 | ✗ | iterateCheck(nodep, "RHS", nodep->rhsp(), SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 1378 | } | ||
| 1379 | } | ||
| 1380 | ✗ | void visit(AstUnbounded* nodep) override { | |
| 1381 | ✗ | nodep->dtypeSetSigned32(); // Used in int context | |
| 1382 | if (VN_IS(nodep->backp(), IsUnbounded)) return; // Ok, leave | ||
| 1383 | if (VN_IS(nodep->backp(), BracketArrayDType)) return; // Ok, leave | ||
| 1384 | if (const auto* const varp = VN_CAST(nodep->backp(), Var)) { | ||
| 1385 | ✗ | if (varp->isParam()) return; // Ok, leave | |
| 1386 | } | ||
| 1387 | AstNode* backp = nodep->backp(); | ||
| 1388 | if (VN_IS(backp, Sub)) backp = backp->backp(); | ||
| 1389 | if (const auto* const selp = VN_CAST(backp, SelExtract)) { | ||
| 1390 | if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; | ||
| 1391 | } | ||
| 1392 | if (const auto* const selp = VN_CAST(backp, SelBit)) { | ||
| 1393 | if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; | ||
| 1394 | } | ||
| 1395 | // queue_slice[#:$] and queue_bitsel[$] etc handled in V3WidthSel | ||
| 1396 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context."); | |
| 1397 | } | ||
| 1398 | ✗ | void visit(AstInferredDisable* nodep) override { | |
| 1399 | ✗ | if (m_vup->prelim()) nodep->dtypeSetBit(); | |
| 1400 | } | ||
| 1401 | ✗ | void visit(AstIsUnbounded* nodep) override { | |
| 1402 | ✗ | if (m_vup->prelim()) { | |
| 1403 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1404 | ✗ | nodep->dtypeSetBit(); | |
| 1405 | } | ||
| 1406 | } | ||
| 1407 | ✗ | void visit(AstUCFunc* nodep) override { | |
| 1408 | // Give it the size the user wants. | ||
| 1409 | ✗ | if (m_vup && m_vup->prelim()) { | |
| 1410 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // We don't care | |
| 1411 | // All arguments seek their natural sizes | ||
| 1412 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 1413 | } | ||
| 1414 | ✗ | if (m_vup->final()) { | |
| 1415 | ✗ | AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); | |
| 1416 | ✗ | nodep->dtypep(expDTypep); // Assume user knows the rules; go with the flow | |
| 1417 | ✗ | if (nodep->width() > 64) { | |
| 1418 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: $c can't generate wider than 64 bits"); | |
| 1419 | } | ||
| 1420 | } | ||
| 1421 | } | ||
| 1422 | ✗ | void visit(AstCLog2* nodep) override { | |
| 1423 | ✗ | if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); | |
| 1424 | } | ||
| 1425 | ✗ | void visit(AstPow* nodep) override { | |
| 1426 | // Pow is special, output sign only depends on LHS sign, but | ||
| 1427 | // function result depends on both signs | ||
| 1428 | // RHS is self-determined (IEEE) | ||
| 1429 | // Real if either side is real (as with AstAdd) | ||
| 1430 | ✗ | if (m_vup->prelim()) { | |
| 1431 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 1432 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 1433 | ✗ | if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { | |
| 1434 | ✗ | spliceCvtD(nodep->lhsp()); | |
| 1435 | ✗ | spliceCvtD(nodep->rhsp()); | |
| 1436 | ✗ | VL_DO_DANGLING(replaceWithDVersion(nodep), nodep); | |
| 1437 | ✗ | return; | |
| 1438 | } | ||
| 1439 | |||
| 1440 | ✗ | checkCvtUS(nodep->lhsp()); | |
| 1441 | ✗ | iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); | |
| 1442 | nodep->dtypeFrom(nodep->lhsp()); | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | ✗ | if (m_vup->final()) { | |
| 1446 | ✗ | AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); | |
| 1447 | ✗ | nodep->dtypep(expDTypep); | |
| 1448 | // rhs already finalized in iterate_shift_prelim | ||
| 1449 | ✗ | iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, nodep->dtypep(), EXTEND_EXP); | |
| 1450 | AstNode* newp = nullptr; // No change | ||
| 1451 | if (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) { | ||
| 1452 | newp = new AstPowSS{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), | ||
| 1453 | ✗ | nodep->rhsp()->unlinkFrBack()}; | |
| 1454 | } else if (nodep->lhsp()->isSigned() && !nodep->rhsp()->isSigned()) { | ||
| 1455 | newp = new AstPowSU{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), | ||
| 1456 | ✗ | nodep->rhsp()->unlinkFrBack()}; | |
| 1457 | } else if (!nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) { | ||
| 1458 | newp = new AstPowUS{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), | ||
| 1459 | ✗ | nodep->rhsp()->unlinkFrBack()}; | |
| 1460 | } | ||
| 1461 | if (newp) { | ||
| 1462 | newp->dtypeFrom(nodep); | ||
| 1463 | ✗ | UINFO(9, "powOld " << nodep << endl); | |
| 1464 | ✗ | UINFO(9, "powNew " << newp << endl); | |
| 1465 | ✗ | nodep->replaceWith(newp); | |
| 1466 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 1467 | } | ||
| 1468 | } | ||
| 1469 | } | ||
| 1470 | ✗ | void visit(AstPowSU* nodep) override { | |
| 1471 | // POWSU/SS/US only created here, dtype already determined, so | ||
| 1472 | // nothing to do in this function | ||
| 1473 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1474 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1475 | } | ||
| 1476 | ✗ | void visit(AstPowSS* nodep) override { | |
| 1477 | // POWSU/SS/US only created here, dtype already determined, so | ||
| 1478 | // nothing to do in this function | ||
| 1479 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1480 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1481 | } | ||
| 1482 | ✗ | void visit(AstPowUS* nodep) override { | |
| 1483 | // POWSU/SS/US only created here, dtype already determined, so | ||
| 1484 | // nothing to do in this function | ||
| 1485 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1486 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1487 | } | ||
| 1488 | ✗ | void visit(AstCountBits* nodep) override { | |
| 1489 | ✗ | if (m_vup->prelim()) { | |
| 1490 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); | |
| 1491 | ✗ | iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); | |
| 1492 | ✗ | iterateCheckSizedSelf(nodep, "THS", nodep->thsp(), SELF, BOTH); | |
| 1493 | ✗ | iterateCheckSizedSelf(nodep, "FHS", nodep->fhsp(), SELF, BOTH); | |
| 1494 | // For widthMin, if a 32 bit number, we need a 6 bit number as we need to return '32'. | ||
| 1495 | ✗ | const int widthMin = V3Number::log2b(nodep->lhsp()->width()) + 1; | |
| 1496 | ✗ | nodep->dtypeSetLogicUnsized(32, widthMin, VSigning::SIGNED); | |
| 1497 | } | ||
| 1498 | } | ||
| 1499 | ✗ | void visit(AstCountOnes* nodep) override { | |
| 1500 | ✗ | if (m_vup->prelim()) { | |
| 1501 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); | |
| 1502 | // For widthMin, if a 32 bit number, we need a 6 bit number as we need to return '32'. | ||
| 1503 | ✗ | const int widthMin = V3Number::log2b(nodep->lhsp()->width()) + 1; | |
| 1504 | ✗ | nodep->dtypeSetLogicUnsized(32, widthMin, VSigning::SIGNED); | |
| 1505 | } | ||
| 1506 | } | ||
| 1507 | ✗ | void visit(AstCvtPackString* nodep) override { | |
| 1508 | if (nodep->didWidthAndSet()) return; | ||
| 1509 | // Opaque returns, so arbitrary | ||
| 1510 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1511 | // Type set in constructor | ||
| 1512 | } | ||
| 1513 | ✗ | void visit(AstTimeImport* nodep) override { | |
| 1514 | // LHS is a real number in seconds | ||
| 1515 | // Need to round to time units and precision | ||
| 1516 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 1517 | const AstConst* const constp = VN_CAST(nodep->lhsp(), Const); | ||
| 1518 | ✗ | UASSERT_OBJ(constp && constp->isDouble(), nodep, "Times should be doubles"); | |
| 1519 | ✗ | UASSERT_OBJ(!nodep->timeunit().isNone(), nodep, "$time import no units"); | |
| 1520 | ✗ | const double timePrescale = constp->num().toDouble(); | |
| 1521 | ✗ | UASSERT_OBJ(!v3Global.rootp()->timeprecision().isNone(), nodep, "Never set precision?"); | |
| 1522 | ✗ | const double time = timePrescale / nodep->timeunit().multiplier(); | |
| 1523 | // IEEE claims you should round to time precision here, but no simulator seems to do this | ||
| 1524 | ✗ | AstConst* const newp = new AstConst{nodep->fileline(), AstConst::RealDouble{}, time}; | |
| 1525 | ✗ | nodep->replaceWith(newp); | |
| 1526 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1527 | } | ||
| 1528 | ✗ | void visit(AstEventControl* nodep) override { | |
| 1529 | ✗ | if (VN_IS(m_ftaskp, Func)) { | |
| 1530 | ✗ | nodep->v3error("Event controls are not legal in functions. Suggest use a task " | |
| 1531 | "(IEEE 1800-2023 13.4.4)"); | ||
| 1532 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 1533 | ✗ | return; | |
| 1534 | } | ||
| 1535 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 1536 | ✗ | if (v3Global.opt.timing().isSetTrue()) { | |
| 1537 | ✗ | iterateChildren(nodep); | |
| 1538 | ✗ | return; | |
| 1539 | ✗ | } else if (v3Global.opt.timing().isSetFalse()) { | |
| 1540 | ✗ | nodep->v3warn(E_NOTIMING, | |
| 1541 | "Event control statement in this location requires --timing\n" | ||
| 1542 | << nodep->warnMore() | ||
| 1543 | << "... With --no-timing, suggest have one event control " | ||
| 1544 | << "statement per procedure, at the top of the procedure"); | ||
| 1545 | } else { | ||
| 1546 | ✗ | nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how " | |
| 1547 | "event controls should be handled"); | ||
| 1548 | } | ||
| 1549 | } | ||
| 1550 | ✗ | if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBack()); | |
| 1551 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 1552 | } | ||
| 1553 | 79812 | void visit(AstAttrOf* nodep) override { | |
| 1554 |
1/2✓ Branch 1 taken 79812 times.
✗ Branch 2 not taken.
|
79812 | VL_RESTORER(m_attrp); |
| 1555 | 79812 | m_attrp = nodep; | |
| 1556 |
3/6✓ Branch 1 taken 79812 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 79812 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 79812 times.
|
79812 | userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); |
| 1557 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 79812 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
79812 | if (nodep->dimp()) userIterateAndNext(nodep->dimp(), WidthVP{SELF, BOTH}.p()); |
| 1558 | // Don't iterate children, don't want to lose VarRef. | ||
| 1559 |
1/6✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 79812 times.
|
79812 | switch (nodep->attrType()) { |
| 1560 | case VAttrType::VAR_BASE: | ||
| 1561 | // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf | ||
| 1562 | break; | ||
| 1563 | ✗ | case VAttrType::DIM_DIMENSIONS: | |
| 1564 | case VAttrType::DIM_UNPK_DIMENSIONS: { | ||
| 1565 | ✗ | UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression"); | |
| 1566 | ✗ | const std::pair<uint32_t, uint32_t> dim = nodep->fromp()->dtypep()->dimensions(true); | |
| 1567 | const int val | ||
| 1568 | ✗ | = (nodep->attrType() == VAttrType::DIM_UNPK_DIMENSIONS ? dim.second | |
| 1569 | ✗ | : (dim.first + dim.second)); | |
| 1570 | ✗ | nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Signed32{}, val)); | |
| 1571 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1572 | break; | ||
| 1573 | } | ||
| 1574 | ✗ | case VAttrType::DIM_BITS: | |
| 1575 | case VAttrType::DIM_HIGH: | ||
| 1576 | case VAttrType::DIM_INCREMENT: | ||
| 1577 | case VAttrType::DIM_LEFT: | ||
| 1578 | case VAttrType::DIM_LOW: | ||
| 1579 | case VAttrType::DIM_RIGHT: | ||
| 1580 | case VAttrType::DIM_SIZE: { | ||
| 1581 | ✗ | UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression"); | |
| 1582 | AstNodeDType* const dtypep = nodep->fromp()->dtypep(); | ||
| 1583 | if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)) { | ||
| 1584 | switch (nodep->attrType()) { | ||
| 1585 | ✗ | case VAttrType::DIM_SIZE: { | |
| 1586 | ✗ | AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr); | |
| 1587 | ✗ | AstNode* const newp = new AstCMethodHard{nodep->fileline(), fromp, "size"}; | |
| 1588 | ✗ | newp->dtypeSetSigned32(); | |
| 1589 | newp->didWidth(true); | ||
| 1590 | newp->protect(false); | ||
| 1591 | ✗ | nodep->replaceWith(newp); | |
| 1592 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1593 | break; | ||
| 1594 | } | ||
| 1595 | ✗ | case VAttrType::DIM_LEFT: | |
| 1596 | case VAttrType::DIM_LOW: { | ||
| 1597 | ✗ | AstNode* const newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, 0); | |
| 1598 | ✗ | nodep->replaceWith(newp); | |
| 1599 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1600 | break; | ||
| 1601 | } | ||
| 1602 | ✗ | case VAttrType::DIM_RIGHT: | |
| 1603 | case VAttrType::DIM_HIGH: { | ||
| 1604 | ✗ | AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr); | |
| 1605 | AstNodeExpr* const sizep | ||
| 1606 | ✗ | = new AstCMethodHard{nodep->fileline(), fromp, "size"}; | |
| 1607 | ✗ | sizep->dtypeSetSigned32(); | |
| 1608 | sizep->didWidth(true); | ||
| 1609 | sizep->protect(false); | ||
| 1610 | AstNode* const newp | ||
| 1611 | = new AstSub{nodep->fileline(), sizep, | ||
| 1612 | ✗ | new AstConst(nodep->fileline(), AstConst::Signed32{}, 1)}; | |
| 1613 | ✗ | nodep->replaceWith(newp); | |
| 1614 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1615 | break; | ||
| 1616 | } | ||
| 1617 | ✗ | case VAttrType::DIM_INCREMENT: { | |
| 1618 | AstNodeExpr* const newp | ||
| 1619 | ✗ | = new AstConst(nodep->fileline(), AstConst::Signed32{}, -1); | |
| 1620 | ✗ | nodep->replaceWith(newp); | |
| 1621 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1622 | break; | ||
| 1623 | } | ||
| 1624 | case VAttrType::DIM_BITS: { | ||
| 1625 | if (VN_IS(dtypep, DynArrayDType)) { | ||
| 1626 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for dynamic array"); | |
| 1627 | } else { | ||
| 1628 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue"); | |
| 1629 | } | ||
| 1630 | break; | ||
| 1631 | } | ||
| 1632 | ✗ | default: nodep->v3fatalSrc("Unhandled attribute type"); | |
| 1633 | } | ||
| 1634 | } else { | ||
| 1635 | ✗ | const std::pair<uint32_t, uint32_t> dimpair = dtypep->skipRefp()->dimensions(true); | |
| 1636 | ✗ | const uint32_t msbdim = dimpair.first + dimpair.second; | |
| 1637 | ✗ | if (!nodep->dimp() || msbdim < 1) { | |
| 1638 | ✗ | if (VN_IS(dtypep, BasicDType) && dtypep->basicp()->isString()) { | |
| 1639 | // IEEE undocumented but $bits(string) must give length(string) * 8 | ||
| 1640 | ✗ | AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr); | |
| 1641 | AstNode* const newp = new AstShiftL{ | ||
| 1642 | ✗ | nodep->fileline(), new AstLenN{nodep->fileline(), fromp}, | |
| 1643 | ✗ | new AstConst{nodep->fileline(), 3}, // * 8 | |
| 1644 | ✗ | 32}; | |
| 1645 | ✗ | nodep->replaceWith(newp); | |
| 1646 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 1647 | } else { | ||
| 1648 | const int dim = 1; | ||
| 1649 | AstConst* const newp | ||
| 1650 | ✗ | = dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim); | |
| 1651 | ✗ | nodep->replaceWith(newp); | |
| 1652 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1653 | } | ||
| 1654 | } else if (VN_IS(nodep->dimp(), Const)) { | ||
| 1655 | ✗ | const int dim = VN_AS(nodep->dimp(), Const)->toSInt(); | |
| 1656 | AstConst* const newp | ||
| 1657 | ✗ | = dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim); | |
| 1658 | ✗ | nodep->replaceWith(newp); | |
| 1659 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1660 | } else { // Need a runtime lookup table. Yuk. | ||
| 1661 | ✗ | UASSERT_OBJ(nodep->fromp() && dtypep, nodep, "Unsized expression"); | |
| 1662 | ✗ | AstVar* const varp = dimensionVarp(dtypep, nodep->attrType(), msbdim); | |
| 1663 | AstNodeExpr* const dimp = nodep->dimp()->unlinkFrBack(); | ||
| 1664 | AstNodeExpr* const newp | ||
| 1665 | ✗ | = new AstArraySel{nodep->fileline(), newVarRefDollarUnit(varp), dimp}; | |
| 1666 | ✗ | nodep->replaceWith(newp); | |
| 1667 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1668 | } | ||
| 1669 | } | ||
| 1670 | break; | ||
| 1671 | } | ||
| 1672 | ✗ | case VAttrType::TYPENAME: { | |
| 1673 | ✗ | UASSERT_OBJ(nodep->fromp(), nodep, "Unprovided expression"); | |
| 1674 | ✗ | const string result = nodep->fromp()->dtypep()->prettyDTypeName(true); | |
| 1675 | ✗ | UINFO(9, "typename '" << result << "' from " << nodep->fromp()->dtypep() << "\n"); | |
| 1676 | ✗ | AstNode* const newp = new AstConst{nodep->fileline(), AstConst::String{}, result}; | |
| 1677 | ✗ | nodep->replaceWith(newp); | |
| 1678 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1679 | break; | ||
| 1680 | } | ||
| 1681 | ✗ | case VAttrType::TYPEID: | |
| 1682 | // Soon to be handled in AstEqT | ||
| 1683 | ✗ | nodep->dtypeSetSigned32(); | |
| 1684 | break; | ||
| 1685 | ✗ | default: { | |
| 1686 | // Everything else resolved earlier | ||
| 1687 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // Approximation, unsized 32 | |
| 1688 | ✗ | UINFO(1, "Missing ATTR type case node: " << nodep << endl); | |
| 1689 | ✗ | nodep->v3fatalSrc("Missing ATTR type case"); | |
| 1690 | break; | ||
| 1691 | } | ||
| 1692 | } | ||
| 1693 | 79812 | } | |
| 1694 | ✗ | void visit(AstPull* nodep) override { | |
| 1695 | // May have select underneath, let seek natural size | ||
| 1696 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 1697 | } | ||
| 1698 | ✗ | void visit(AstText* nodep) override { | |
| 1699 | // Only used in CStmts which don't care.... | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | // DTYPES | ||
| 1703 | ✗ | void visit(AstNodeArrayDType* nodep) override { | |
| 1704 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1705 | |||
| 1706 | ✗ | if (nodep->subDTypep() == nodep->basicp()) { // Innermost dimension | |
| 1707 | ✗ | AstBasicDType* const basicp = nodep->basicp(); | |
| 1708 | // If basic dtype is LOGIC_IMPLICIT, it is actually 1 bit LOGIC | ||
| 1709 | ✗ | if (basicp->implicit()) { | |
| 1710 | ✗ | UASSERT_OBJ(basicp->width() <= 1, basicp, | |
| 1711 | "must be 1 bit but actually " << basicp->width() << " bits"); | ||
| 1712 | AstBasicDType* const newp = new AstBasicDType{ | ||
| 1713 | ✗ | basicp->fileline(), VBasicDTypeKwd::LOGIC, basicp->numeric()}; | |
| 1714 | newp->widthForce(1, 1); | ||
| 1715 | ✗ | basicp->replaceWith(newp); | |
| 1716 | VL_DO_DANGLING(pushDeletep(basicp), basicp); | ||
| 1717 | } | ||
| 1718 | } | ||
| 1719 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 1720 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1721 | // Cleanup array size | ||
| 1722 | ✗ | userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p()); | |
| 1723 | nodep->dtypep(nodep); // The array itself, not subDtype | ||
| 1724 | if (auto* const adtypep = VN_CAST(nodep, UnpackArrayDType)) { | ||
| 1725 | // Historically array elements have width of the ref type not the full array | ||
| 1726 | ✗ | nodep->widthFromSub(nodep->subDTypep()); | |
| 1727 | ✗ | if (nodep->subDTypep()->skipRefp()->isCompound()) adtypep->isCompound(true); | |
| 1728 | } else { | ||
| 1729 | ✗ | const int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst(); | |
| 1730 | nodep->widthForce(width, width); | ||
| 1731 | } | ||
| 1732 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1733 | } | ||
| 1734 | ✗ | void visit(AstAssocArrayDType* nodep) override { | |
| 1735 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1736 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 1737 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1738 | ✗ | nodep->keyDTypep(iterateEditMoveDTypep(nodep, nodep->keyDTypep())); | |
| 1739 | nodep->dtypep(nodep); // The array itself, not subDtype | ||
| 1740 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1741 | } | ||
| 1742 | ✗ | void visit(AstBracketArrayDType* nodep) override { | |
| 1743 | // Type inserted only because parser didn't know elementsp() type | ||
| 1744 | // Resolve elementsp's type | ||
| 1745 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 1746 | // We must edit when dtype still under normal nodes and before type table | ||
| 1747 | // See notes in iterateEditMoveDTypep | ||
| 1748 | AstNodeDType* const childp = nodep->childDTypep(); | ||
| 1749 | childp->unlinkFrBack(); | ||
| 1750 | ✗ | AstNode* const elementsp = nodep->elementsp()->unlinkFrBack(); | |
| 1751 | AstNode* newp; | ||
| 1752 | if (VN_IS(elementsp, Unbounded)) { | ||
| 1753 | ✗ | newp = new AstQueueDType{nodep->fileline(), VFlagChildDType{}, childp, nullptr}; | |
| 1754 | ✗ | VL_DO_DANGLING(elementsp->deleteTree(), elementsp); | |
| 1755 | } else if (AstNodeDType* const keyp = VN_CAST(elementsp, NodeDType)) { | ||
| 1756 | ✗ | newp = new AstAssocArrayDType{nodep->fileline(), VFlagChildDType{}, childp, keyp}; | |
| 1757 | } else { | ||
| 1758 | // The subtract in the range may confuse users; as the array | ||
| 1759 | // size is self determined there's no reason to warn about widths | ||
| 1760 | FileLine* const elementsNewFl = elementsp->fileline(); | ||
| 1761 | ✗ | elementsNewFl->warnOff(V3ErrorCode::WIDTHEXPAND, true); | |
| 1762 | // Must be expression that is constant, but we'll determine that later | ||
| 1763 | newp = new AstUnpackArrayDType{ | ||
| 1764 | nodep->fileline(), VFlagChildDType{}, childp, | ||
| 1765 | ✗ | new AstRange{nodep->fileline(), new AstConst(elementsp->fileline(), 0), | |
| 1766 | new AstSub{elementsNewFl, VN_AS(elementsp, NodeExpr), | ||
| 1767 | ✗ | new AstConst(elementsNewFl, 1)}}}; | |
| 1768 | } | ||
| 1769 | ✗ | nodep->replaceWith(newp); | |
| 1770 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 1771 | // Normally parent's iteration would cover this, but we might have entered by a specific | ||
| 1772 | // visit | ||
| 1773 | VL_DO_DANGLING(userIterate(newp, nullptr), newp); | ||
| 1774 | } | ||
| 1775 | ✗ | void visit(AstConstraintRefDType* nodep) override { | |
| 1776 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1777 | nodep->dtypep(nodep); | ||
| 1778 | } | ||
| 1779 | ✗ | void visit(AstDynArrayDType* nodep) override { | |
| 1780 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1781 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 1782 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1783 | nodep->dtypep(nodep); // The array itself, not subDtype | ||
| 1784 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1785 | } | ||
| 1786 | ✗ | void visit(AstQueueDType* nodep) override { | |
| 1787 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1788 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 1789 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1790 | nodep->dtypep(nodep); // The array itself, not subDtype | ||
| 1791 | ✗ | userIterateAndNext(nodep->boundp(), WidthVP{SELF, BOTH}.p()); | |
| 1792 | if (VN_IS(nodep->boundp(), Unbounded)) { | ||
| 1793 | ✗ | nodep->boundp()->unlinkFrBack()->deleteTree(); // nullptr will represent unbounded | |
| 1794 | } | ||
| 1795 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1796 | } | ||
| 1797 |
1/2✓ Branch 0 taken 485 times.
✗ Branch 1 not taken.
|
485 | void visit(AstVoidDType* nodep) override { |
| 1798 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1799 | nodep->dtypep(nodep); | ||
| 1800 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 485 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
485 | UINFO(4, "dtWidthed " << nodep << endl); |
| 1801 | } | ||
| 1802 | ✗ | void visit(AstUnsizedArrayDType* nodep) override { | |
| 1803 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1804 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 1805 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1806 | // Cleanup array size | ||
| 1807 | nodep->dtypep(nodep); // The array itself, not subDtype | ||
| 1808 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1809 | } | ||
| 1810 | ✗ | void visit(AstWildcardArrayDType* nodep) override { | |
| 1811 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1812 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 1813 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1814 | // Cleanup array size | ||
| 1815 | nodep->dtypep(nodep); // The array itself, not subDtype | ||
| 1816 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1817 | } | ||
| 1818 |
2/2✓ Branch 0 taken 171315 times.
✓ Branch 1 taken 167025 times.
|
338340 | void visit(AstBasicDType* nodep) override { |
| 1819 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1820 |
2/2✓ Branch 0 taken 81960 times.
✓ Branch 1 taken 89355 times.
|
171315 | if (nodep->generic()) return; // Already perfect |
| 1821 |
2/2✓ Branch 0 taken 77784 times.
✓ Branch 1 taken 4176 times.
|
81960 | if (nodep->rangep()) { |
| 1822 | 77784 | userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p()); | |
| 1823 | // Because this DType has a unique child range, we know it's not | ||
| 1824 | // pointed at by other nodes unless they are referencing this type. | ||
| 1825 | // Furthermore the width() calculation would return identical | ||
| 1826 | // values. Therefore we can directly replace the width | ||
| 1827 | 77784 | nodep->widthForce(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst()); | |
| 1828 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4176 times.
|
4176 | } else if (nodep->isRanged()) { |
| 1829 | nodep->widthForce(nodep->nrange().elements(), nodep->nrange().elements()); | ||
| 1830 |
1/2✓ Branch 0 taken 4176 times.
✗ Branch 1 not taken.
|
4176 | } else if (nodep->implicit()) { |
| 1831 | // Parameters may notice implicitness and change to different dtype | ||
| 1832 | nodep->widthForce(1, 1); | ||
| 1833 | } | ||
| 1834 | // else width in node is correct; it was set based on keyword().width() | ||
| 1835 | // at construction time. Ditto signed, so "unsigned byte" etc works right. | ||
| 1836 | 81960 | nodep->cvtRangeConst(); | |
| 1837 | // TODO: If BasicDType now looks like a generic type, we can convert to a real generic | ||
| 1838 | // dtype Instead for now doing this in V3WidthCommit | ||
| 1839 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 81960 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
81960 | UINFO(4, "dtWidthed " << nodep << endl); |
| 1840 | } | ||
| 1841 | ✗ | void visit(AstConstDType* nodep) override { | |
| 1842 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1843 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 1844 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1845 | userIterateChildren(nodep, nullptr); | ||
| 1846 | nodep->dtypep(nodep); // Should already be set, but be clear it's not the subDType | ||
| 1847 | nodep->widthFromSub(nodep->subDTypep()); | ||
| 1848 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1849 | } | ||
| 1850 | ✗ | void visit(AstRefDType* nodep) override { | |
| 1851 | ✗ | if (nodep->doingWidth()) { // Early exit if have circular parameter definition | |
| 1852 | ✗ | nodep->v3error("Typedef's type is circular: " << nodep->prettyName()); | |
| 1853 | ✗ | nodep->dtypeSetBit(); | |
| 1854 | nodep->doingWidth(false); | ||
| 1855 | ✗ | return; | |
| 1856 | } | ||
| 1857 | ✗ | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | |
| 1858 | nodep->doingWidth(true); | ||
| 1859 | ✗ | if (nodep->typeofp()) { // type(typeofp_expression) | |
| 1860 | if (AstNodeDType* typeofDtp = VN_CAST(nodep->typeofp(), NodeDType)) { | ||
| 1861 | // It's directly a type, e.g. "type(int)" | ||
| 1862 | ✗ | typeofDtp = iterateEditMoveDTypep(nodep, typeofDtp); // Changes typeofp | |
| 1863 | nodep->refDTypep(typeofDtp); | ||
| 1864 | } else { | ||
| 1865 | // Type comes from expression's type, e.g. "type(variable)" | ||
| 1866 | ✗ | userIterateAndNext(nodep->typeofp(), WidthVP{SELF, BOTH}.p()); | |
| 1867 | AstNode* const typeofp = nodep->typeofp(); | ||
| 1868 | nodep->refDTypep(typeofp->dtypep()); | ||
| 1869 | ✗ | VL_DO_DANGLING(typeofp->unlinkFrBack()->deleteTree(), typeofp); | |
| 1870 | } | ||
| 1871 | // We had to use AstRefDType for this construct as pointers to this type | ||
| 1872 | // in type table are still correct (which they wouldn't be if we replaced the node) | ||
| 1873 | } | ||
| 1874 | userIterateChildren(nodep, nullptr); | ||
| 1875 | ✗ | if (nodep->subDTypep()) { | |
| 1876 | // Normally iterateEditMoveDTypep iterate would work, but the refs are under | ||
| 1877 | // the TypeDef which will upset iterateEditMoveDTypep as it can't find it under | ||
| 1878 | // this node's childDTypep | ||
| 1879 | ✗ | userIterate(nodep->subDTypep(), nullptr); | |
| 1880 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1881 | // Widths are resolved, but special iterate to check for recursion | ||
| 1882 | ✗ | userIterate(nodep->subDTypep(), nullptr); | |
| 1883 | } | ||
| 1884 | // Effectively nodep->dtypeFrom(nodep->dtypeSkipRefp()); | ||
| 1885 | // But might be recursive, so instead manually recurse into the referenced type | ||
| 1886 | ✗ | UASSERT_OBJ(nodep->subDTypep(), nodep, "Unlinked"); | |
| 1887 | ✗ | nodep->dtypeFrom(nodep->subDTypep()); | |
| 1888 | ✗ | nodep->widthFromSub(nodep->subDTypep()); | |
| 1889 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 1890 | // No nodep->typedefp(nullptr) for now; V3WidthCommit needs to check accesses | ||
| 1891 | nodep->doingWidth(false); | ||
| 1892 | } | ||
| 1893 | ✗ | void visit(AstTypedef* nodep) override { | |
| 1894 | ✗ | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | |
| 1895 | ✗ | if (auto* const refp = checkRefToTypedefRecurse(nodep, nodep)) { | |
| 1896 | ✗ | nodep->v3error("Typedef has self-reference: " << nodep->prettyNameQ() << '\n' | |
| 1897 | << nodep->warnContextPrimary() << '\n' | ||
| 1898 | << refp->warnOther() | ||
| 1899 | << "... Location of reference\n" | ||
| 1900 | << refp->warnContextSecondary()); | ||
| 1901 | // May cause internal error but avoids infinite loop on dump | ||
| 1902 | refp->typedefp(nullptr); | ||
| 1903 | VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
| 1904 | ✗ | return; | |
| 1905 | } | ||
| 1906 | ✗ | nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1907 | userIterateChildren(nodep, nullptr); | ||
| 1908 | } | ||
| 1909 | ✗ | void visit(AstParamTypeDType* nodep) override { | |
| 1910 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 1911 | ✗ | nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1912 | userIterateChildren(nodep, nullptr); | ||
| 1913 | nodep->widthFromSub(nodep->subDTypep()); | ||
| 1914 | } | ||
| 1915 | ✗ | void visit(AstCastDynamic* nodep) override { | |
| 1916 | ✗ | nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 1917 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 1918 | AstNodeDType* const toDtp = nodep->top()->dtypep()->skipRefToEnump(); | ||
| 1919 | AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); | ||
| 1920 | FileLine* const fl = nodep->fileline(); | ||
| 1921 | ✗ | const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp()); | |
| 1922 | AstNode* newp; | ||
| 1923 | ✗ | if (castable == VCastable::DYNAMIC_CLASS) { | |
| 1924 | // Keep in place, will compute at runtime | ||
| 1925 | ✗ | return; | |
| 1926 | ✗ | } else if (castable == VCastable::ENUM_EXPLICIT || castable == VCastable::ENUM_IMPLICIT) { | |
| 1927 | // TODO is from is a constant we could simplify, though normal constant | ||
| 1928 | // elimination should do much the same | ||
| 1929 | // Form: "( ((v > size) ? false : enum_valid[v[N:0]]) | ||
| 1930 | // ? ExprStmt(ExprAssign(out, Cast(v, type)), 1) : 0)" | ||
| 1931 | ✗ | AstEnumDType* const enumDtp = VN_AS(toDtp, EnumDType); | |
| 1932 | ✗ | UASSERT_OBJ(enumDtp, nodep, "$cast determined as enum, but not enum type"); | |
| 1933 | AstNodeExpr* const testp | ||
| 1934 | ✗ | = enumTestValid(nodep->fromp()->cloneTreePure(false), enumDtp); | |
| 1935 | ✗ | FileLine* const fl_novalue = new FileLine{fl}; | |
| 1936 | ✗ | fl_novalue->warnOff(V3ErrorCode::ENUMVALUE, true); | |
| 1937 | newp = new AstCond{ | ||
| 1938 | fl, testp, | ||
| 1939 | new AstExprStmt{fl, | ||
| 1940 | new AstAssign{fl_novalue, nodep->top()->unlinkFrBack(), | ||
| 1941 | ✗ | nodep->fromp()->unlinkFrBack()}, | |
| 1942 | ✗ | new AstConst{fl, AstConst::Signed32{}, 1}}, | |
| 1943 | ✗ | new AstConst{fl, AstConst::Signed32{}, 0}}; | |
| 1944 | ✗ | } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) { | |
| 1945 | ✗ | nodep->v3warn(CASTCONST, "$cast will always return one as " | |
| 1946 | << toDtp->prettyDTypeNameQ() | ||
| 1947 | << " is always castable from " | ||
| 1948 | << fromDtp->prettyDTypeNameQ() << '\n' | ||
| 1949 | << nodep->warnMore() << "... Suggest static cast"); | ||
| 1950 | newp = new AstExprStmt{ | ||
| 1951 | fl, | ||
| 1952 | new AstAssign{fl, nodep->top()->unlinkFrBack(), | ||
| 1953 | ✗ | new AstCast{fl, nodep->fromp()->unlinkFrBack(), toDtp}}, | |
| 1954 | ✗ | new AstConst{fl, AstConst::Signed32{}, 1}}; | |
| 1955 | ✗ | } else if (castable == VCastable::INCOMPATIBLE) { | |
| 1956 | ✗ | newp = new AstConst{fl, 0}; | |
| 1957 | ✗ | nodep->v3warn(CASTCONST, "$cast will always return zero as " | |
| 1958 | << toDtp->prettyDTypeNameQ() << " is not castable from " | ||
| 1959 | << fromDtp->prettyDTypeNameQ()); | ||
| 1960 | } else { | ||
| 1961 | ✗ | newp = new AstConst{fl, 0}; | |
| 1962 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: $cast to " | |
| 1963 | << toDtp->prettyDTypeNameQ() << " from " | ||
| 1964 | << fromDtp->prettyDTypeNameQ() << '\n' | ||
| 1965 | << nodep->warnMore() | ||
| 1966 | << "... Suggest try static cast"); | ||
| 1967 | } | ||
| 1968 | newp->dtypeFrom(nodep); | ||
| 1969 | ✗ | nodep->replaceWith(newp); | |
| 1970 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 1971 | ✗ | userIterate(newp, m_vup); | |
| 1972 | } | ||
| 1973 | ✗ | void visit(AstCastParse* nodep) override { | |
| 1974 | // nodep->dtp could be data type, or a primary_constant | ||
| 1975 | // Don't iterate lhsp, will deal with that once convert the type | ||
| 1976 | ✗ | V3Const::constifyParamsEdit(nodep->dtp()); // itemp may change | |
| 1977 | if (AstConst* const constp = VN_CAST(nodep->dtp(), Const)) { | ||
| 1978 | constp->unlinkFrBack(); | ||
| 1979 | AstNode* const newp | ||
| 1980 | ✗ | = new AstCastSize{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), constp}; | |
| 1981 | ✗ | nodep->replaceWith(newp); | |
| 1982 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 1983 | ✗ | userIterate(newp, m_vup); | |
| 1984 | } else { | ||
| 1985 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 1986 | "Unsupported: Cast to " << nodep->dtp()->prettyTypeName()); | ||
| 1987 | ✗ | nodep->replaceWith(nodep->lhsp()->unlinkFrBack()); | |
| 1988 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 1989 | } | ||
| 1990 | } | ||
| 1991 | ✗ | void visit(AstCast* nodep) override { | |
| 1992 | ✗ | if (nodep->didWidth()) return; | |
| 1993 | ✗ | UINFO(9, "CAST " << nodep << endl); | |
| 1994 | ✗ | nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 1995 | ✗ | if (m_vup->prelim()) { | |
| 1996 | ✗ | if (debug() >= 9) nodep->dumpTree("- CastPre: "); | |
| 1997 | // if (debug()) nodep->backp()->dumpTree("- CastPreUpUp: "); | ||
| 1998 | ✗ | userIterateAndNext(nodep->fromp(), WidthVP{SELF, PRELIM}.p()); | |
| 1999 | AstNodeDType* const toDtp = nodep->dtypep()->skipRefToEnump(); | ||
| 2000 | AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); | ||
| 2001 | ✗ | const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp()); | |
| 2002 | bool bad = false; | ||
| 2003 | ✗ | if (castable == VCastable::UNSUPPORTED) { | |
| 2004 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: static cast to " | |
| 2005 | << toDtp->prettyDTypeNameQ() << " from " | ||
| 2006 | << fromDtp->prettyDTypeNameQ()); | ||
| 2007 | bad = true; | ||
| 2008 | ✗ | } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE | |
| 2009 | ✗ | || castable == VCastable::ENUM_IMPLICIT | |
| 2010 | ✗ | || castable == VCastable::ENUM_EXPLICIT) { | |
| 2011 | ; // Continue | ||
| 2012 | ✗ | } else if (castable == VCastable::DYNAMIC_CLASS) { | |
| 2013 | ✗ | nodep->v3error("Dynamic, not static cast, required to cast " | |
| 2014 | << toDtp->prettyDTypeNameQ() << " from " | ||
| 2015 | << fromDtp->prettyDTypeNameQ() << '\n' | ||
| 2016 | << nodep->warnMore() << "... Suggest dynamic $cast"); | ||
| 2017 | bad = true; | ||
| 2018 | ✗ | } else if (castable == VCastable::INCOMPATIBLE) { | |
| 2019 | ✗ | nodep->v3error("Incompatible types to static cast to " | |
| 2020 | << toDtp->prettyDTypeNameQ() << " from " | ||
| 2021 | << fromDtp->prettyDTypeNameQ() << '\n'); | ||
| 2022 | bad = true; | ||
| 2023 | } else { | ||
| 2024 | ✗ | nodep->v3fatalSrc("bad casting case"); | |
| 2025 | } | ||
| 2026 | // For now, replace it ASAP, so widthing can propagate easily | ||
| 2027 | // The cast may change signing, but we don't know the sign yet. Make it so. | ||
| 2028 | // Note we don't sign fromp() that would make the algorithm O(n^2) if lots of casting. | ||
| 2029 | AstNodeExpr* newp = nullptr; | ||
| 2030 | if (bad) { | ||
| 2031 | ✗ | } else if (const AstBasicDType* const basicp = toDtp->basicp()) { | |
| 2032 | ✗ | if (!basicp->isString() && fromDtp->isString()) { | |
| 2033 | ✗ | newp = new AstNToI{nodep->fileline(), nodep->fromp()->unlinkFrBack(), toDtp}; | |
| 2034 | ✗ | } else if (!basicp->isDouble() && !fromDtp->isDouble()) { | |
| 2035 | AstNodeDType* const origDTypep = nodep->dtypep(); | ||
| 2036 | if (!VN_IS(fromDtp, StreamDType)) { | ||
| 2037 | const int width = toDtp->width(); | ||
| 2038 | ✗ | castSized(nodep, nodep->fromp(), width); | |
| 2039 | } | ||
| 2040 | nodep->dtypeFrom(origDTypep); // If was enum, need dtype to preserve as enum | ||
| 2041 | // Note castSized might modify nodep->fromp() | ||
| 2042 | } else { | ||
| 2043 | ✗ | iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, fromDtp, EXTEND_EXP, | |
| 2044 | false); | ||
| 2045 | } | ||
| 2046 | if (newp) { | ||
| 2047 | ✗ | } else if (basicp->isDouble() && !nodep->fromp()->isDouble()) { | |
| 2048 | if (nodep->fromp()->isSigned()) { | ||
| 2049 | ✗ | newp = new AstISToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 2050 | } else { | ||
| 2051 | ✗ | newp = new AstIToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 2052 | } | ||
| 2053 | ✗ | } else if (!basicp->isDouble() && nodep->fromp()->isDouble()) { | |
| 2054 | ✗ | newp = new AstRToIRoundS{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 2055 | ✗ | newp->dtypeChgSigned(basicp->isSigned()); | |
| 2056 | ✗ | } else if (basicp->isSigned() && !nodep->fromp()->isSigned()) { | |
| 2057 | ✗ | newp = new AstSigned{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 2058 | ✗ | } else if (!basicp->isSigned() && nodep->fromp()->isSigned()) { | |
| 2059 | ✗ | newp = new AstUnsigned{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 2060 | } else { | ||
| 2061 | // Can just remove cast, but need extend placeholder | ||
| 2062 | // so we can avoid warning message | ||
| 2063 | } | ||
| 2064 | } else if (VN_IS(toDtp, ClassRefDType)) { | ||
| 2065 | // Can just remove cast | ||
| 2066 | } else { | ||
| 2067 | ✗ | nodep->v3fatalSrc("Unimplemented: Casting non-simple data type " | |
| 2068 | << toDtp->prettyDTypeNameQ()); | ||
| 2069 | } | ||
| 2070 | ✗ | if (!newp) newp = nodep->fromp()->unlinkFrBack(); | |
| 2071 | nodep->fromp(newp); | ||
| 2072 | ✗ | if (debug() >= 9) nodep->dumpTree("- CastOut: "); | |
| 2073 | // if (debug()) nodep->backp()->dumpTree("- CastOutUpUp: "); | ||
| 2074 | } | ||
| 2075 | ✗ | if (m_vup->final()) { | |
| 2076 | ✗ | if (debug() >= 9) nodep->dumpTree(cout, "- CastFPit: "); | |
| 2077 | ✗ | iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, nodep->fromp()->dtypep(), | |
| 2078 | EXTEND_EXP, false); | ||
| 2079 | ✗ | if (debug() >= 9) nodep->dumpTree("- CastFin: "); | |
| 2080 | AstNodeExpr* const underp = nodep->fromp()->unlinkFrBack(); | ||
| 2081 | underp->dtypeFrom(nodep); | ||
| 2082 | underp->didWidth(true); | ||
| 2083 | ✗ | AstNodeExpr* const newp = new AstCastWrap{nodep->fileline(), underp}; | |
| 2084 | newp->didWidth(true); | ||
| 2085 | ✗ | if (debug() >= 9) newp->dumpTree("- CastRep: "); | |
| 2086 | ✗ | nodep->replaceWith(newp); | |
| 2087 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2088 | } | ||
| 2089 | } | ||
| 2090 | ✗ | void visit(AstCastSize* nodep) override { | |
| 2091 | // IEEE: Signedness of result is same as self-determined signedness | ||
| 2092 | // However, the result is same as BITSEL, so we do not sign extend the LHS | ||
| 2093 | // if (debug()) nodep->dumpTree("- CastSizePre: "); | ||
| 2094 | ✗ | if (m_vup->prelim()) { | |
| 2095 | int width = nodep->rhsp()->toSInt(); | ||
| 2096 | ✗ | if (width < 1) { | |
| 2097 | ✗ | nodep->v3error("Size-changing cast to zero or negative size"); | |
| 2098 | width = 1; | ||
| 2099 | } | ||
| 2100 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); | |
| 2101 | ✗ | castSized(nodep, nodep->lhsp(), width); // lhsp may change | |
| 2102 | } | ||
| 2103 | ✗ | if (m_vup->final()) { | |
| 2104 | // CastSize not needed once sizes determined | ||
| 2105 | AstNode* const underp = nodep->lhsp()->unlinkFrBack(); | ||
| 2106 | underp->dtypeFrom(nodep); | ||
| 2107 | ✗ | nodep->replaceWith(underp); | |
| 2108 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2109 | } | ||
| 2110 | // if (debug()) nodep->dumpTree("- CastSizeOut: "); | ||
| 2111 | } | ||
| 2112 | ✗ | void visit(AstCastWrap* nodep) override { | |
| 2113 | // Inserted by V3Width only so we know has been resolved | ||
| 2114 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{nodep->dtypep(), BOTH}.p()); | |
| 2115 | } | ||
| 2116 | ✗ | void castSized(AstNode* nodep, AstNode* underp, int width) { | |
| 2117 | const AstBasicDType* underDtp = VN_CAST(underp->dtypep(), BasicDType); | ||
| 2118 | ✗ | if (!underDtp) underDtp = underp->dtypep()->basicp(); | |
| 2119 | ✗ | if (!underDtp) { | |
| 2120 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: Size-changing cast on non-basic data type"); | |
| 2121 | ✗ | underDtp = VN_AS(nodep->findBitDType(), BasicDType); | |
| 2122 | } | ||
| 2123 | ✗ | UASSERT_OBJ(underp == nodep->op1p(), nodep, "Assuming op1 is cast value"); | |
| 2124 | // A cast propagates its size to the lower expression and is included in the maximum | ||
| 2125 | // width, so 23'(1'b1 + 1'b1) uses 23-bit math, but 1'(2'h2 * 2'h1) uses two-bit math. | ||
| 2126 | // However the output width is exactly that requested. | ||
| 2127 | // So two steps, first do the calculation's width (max of the two widths) | ||
| 2128 | { | ||
| 2129 | ✗ | const int calcWidth = std::max(width, underDtp->width()); | |
| 2130 | AstNodeDType* const calcDtp | ||
| 2131 | = (underDtp->isFourstate() | ||
| 2132 | ✗ | ? nodep->findLogicDType(calcWidth, calcWidth, underDtp->numeric()) | |
| 2133 | ✗ | : nodep->findBitDType(calcWidth, calcWidth, underDtp->numeric())); | |
| 2134 | nodep->dtypep(calcDtp); | ||
| 2135 | // We ignore warnings as that is sort of the point of a cast | ||
| 2136 | ✗ | iterateCheck(nodep, "Cast expr", underp, CONTEXT_DET, FINAL, calcDtp, EXTEND_EXP, | |
| 2137 | false); | ||
| 2138 | VL_DANGLING(underp); | ||
| 2139 | underp = nodep->op1p(); // Above asserts that op1 was underp pre-relink | ||
| 2140 | } | ||
| 2141 | // if (debug()) nodep->dumpTree("- CastSizeClc: "); | ||
| 2142 | // Next step, make the proper output width | ||
| 2143 | { | ||
| 2144 | AstNodeDType* const outDtp | ||
| 2145 | = (underDtp->isFourstate() | ||
| 2146 | ✗ | ? nodep->findLogicDType(width, width, underDtp->numeric()) | |
| 2147 | ✗ | : nodep->findBitDType(width, width, underDtp->numeric())); | |
| 2148 | nodep->dtypep(outDtp); | ||
| 2149 | // We ignore warnings as that is sort of the point of a cast | ||
| 2150 | ✗ | widthCheckSized(nodep, "Cast expr", VN_AS(underp, NodeExpr), outDtp, EXTEND_EXP, | |
| 2151 | false); | ||
| 2152 | VL_DANGLING(underp); | ||
| 2153 | } | ||
| 2154 | } | ||
| 2155 | ✗ | void visit(AstConstraintBefore* nodep) override { | |
| 2156 | ✗ | userIterateAndNext(nodep->lhssp(), WidthVP{SELF, BOTH}.p()); | |
| 2157 | ✗ | userIterateAndNext(nodep->rhssp(), WidthVP{SELF, BOTH}.p()); | |
| 2158 | } | ||
| 2159 | ✗ | void visit(AstConstraintExpr* nodep) override { | |
| 2160 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p()); | |
| 2161 | } | ||
| 2162 | ✗ | void visit(AstConstraintUnique* nodep) override { | |
| 2163 | ✗ | userIterateAndNext(nodep->rangesp(), WidthVP{SELF, BOTH}.p()); | |
| 2164 | } | ||
| 2165 | ✗ | void visit(AstDistItem* nodep) override { | |
| 2166 | ✗ | userIterate(nodep->rangep(), m_vup); | |
| 2167 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 2168 | ✗ | userIterate(nodep->weightp(), WidthVP{SELF, BOTH}.p()); | |
| 2169 | } | ||
| 2170 | nodep->dtypep(nodep->rangep()->dtypep()); | ||
| 2171 | } | ||
| 2172 |
1/2✓ Branch 0 taken 82406 times.
✗ Branch 1 not taken.
|
82406 | void visit(AstVar* nodep) override { |
| 2173 | // if (debug()) nodep->dumpTree("- InitPre: "); | ||
| 2174 | // Must have deterministic constant width | ||
| 2175 | // We can't skip this step when width()!=0, as creating a AstVar | ||
| 2176 | // with non-constant range gets size 1, not size 0. So use didWidth(). | ||
| 2177 |
1/2✓ Branch 0 taken 82406 times.
✗ Branch 1 not taken.
|
82406 | if (nodep->didWidth()) return; |
| 2178 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82406 times.
|
82406 | if (nodep->doingWidth()) { // Early exit if have circular parameter definition |
| 2179 | ✗ | UASSERT_OBJ(nodep->valuep(), nodep, "circular, but without value"); | |
| 2180 | ✗ | nodep->v3error("Variable's initial value is circular: " << nodep->prettyNameQ()); | |
| 2181 | ✗ | pushDeletep(nodep->valuep()->unlinkFrBack()); | |
| 2182 | ✗ | nodep->valuep(new AstConst{nodep->fileline(), AstConst::BitTrue{}}); | |
| 2183 | nodep->dtypeFrom(nodep->valuep()); | ||
| 2184 | nodep->didWidth(true); | ||
| 2185 | ✗ | return; | |
| 2186 | } | ||
| 2187 | nodep->doingWidth(true); | ||
| 2188 | // Make sure dtype is sized | ||
| 2189 |
2/2✓ Branch 0 taken 80095 times.
✓ Branch 1 taken 2311 times.
|
162501 | nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); |
| 2190 |
1/8✗ Branch 0 not taken.
✓ Branch 1 taken 82406 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
82406 | UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype determined for var"); |
| 2191 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 82406 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
82406 | if (m_ftaskp && m_ftaskp->dpiImport()) { |
| 2192 | AstNodeDType* dtp = nodep->dtypep(); | ||
| 2193 | AstNodeDType* np = nullptr; | ||
| 2194 | while (VN_IS(dtp->skipRefp(), DynArrayDType) | ||
| 2195 | || VN_IS(dtp->skipRefp(), UnpackArrayDType)) { | ||
| 2196 | if (const AstDynArrayDType* const unsizedp | ||
| 2197 | = VN_CAST(dtp->skipRefp(), DynArrayDType)) { | ||
| 2198 | ✗ | if (!np) { | |
| 2199 | ✗ | UINFO(9, "Dynamic becomes unsized array (var itself) " << nodep << endl); | |
| 2200 | } else { | ||
| 2201 | ✗ | UINFO(9, "Dynamic becomes unsized array (subDType) " << dtp << endl); | |
| 2202 | } | ||
| 2203 | AstUnsizedArrayDType* const newp | ||
| 2204 | ✗ | = new AstUnsizedArrayDType{unsizedp->fileline(), unsizedp->subDTypep()}; | |
| 2205 | newp->dtypep(newp); | ||
| 2206 | ✗ | if (!np) { // for Var itself | |
| 2207 | nodep->dtypep(newp); | ||
| 2208 | } else { // for subDType | ||
| 2209 | ✗ | np->virtRefDTypep(newp); | |
| 2210 | } | ||
| 2211 | v3Global.rootp()->typeTablep()->addTypesp(newp); | ||
| 2212 | np = newp; | ||
| 2213 | } else { | ||
| 2214 | np = dtp->skipRefp(); | ||
| 2215 | } | ||
| 2216 | ✗ | dtp = np->virtRefDTypep(); | |
| 2217 | } | ||
| 2218 | } | ||
| 2219 | if (AstWildcardArrayDType* const wildp | ||
| 2220 | = VN_CAST(nodep->dtypeSkipRefp(), WildcardArrayDType)) { | ||
| 2221 | nodep->dtypep(wildp); // Skip RefDType like for other dynamic array types | ||
| 2222 | } | ||
| 2223 | if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) nodep->isConst(true); | ||
| 2224 | // Parameters if implicit untyped inherit from what they are assigned to | ||
| 2225 | const AstBasicDType* const bdtypep = VN_CAST(nodep->dtypep(), BasicDType); | ||
| 2226 | bool didchk = false; | ||
| 2227 |
5/6✓ Branch 0 taken 4622 times.
✓ Branch 1 taken 77784 times.
✓ Branch 2 taken 4622 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2311 times.
✓ Branch 5 taken 2311 times.
|
82406 | const bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit(); |
| 2228 | if (implicitParam) { | ||
| 2229 |
1/2✓ Branch 0 taken 2311 times.
✗ Branch 1 not taken.
|
2311 | if (nodep->valuep()) { |
| 2230 | 2311 | userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 2231 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 2311 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
2311 | UINFO(9, "implicitParamPRELIMIV " << nodep->valuep() << endl); |
| 2232 | // Although nodep will get a different width for parameters | ||
| 2233 | // just below, we want the init numbers to retain their | ||
| 2234 | // width/minwidth until parameters are replaced. | ||
| 2235 | // This prevents width warnings at the location the parameter is substituted in | ||
| 2236 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2311 times.
|
2311 | if (nodep->valuep()->isDouble()) { |
| 2237 | ✗ | nodep->dtypeSetDouble(); | |
| 2238 | VL_DANGLING(bdtypep); | ||
| 2239 | } else { | ||
| 2240 | int width = 0; | ||
| 2241 | 2311 | const AstBasicDType* const valueBdtypep = nodep->valuep()->dtypep()->basicp(); | |
| 2242 | bool issigned = false; | ||
| 2243 |
1/2✓ Branch 0 taken 2311 times.
✗ Branch 1 not taken.
|
2311 | if (bdtypep->isNosign()) { |
| 2244 |
2/4✓ Branch 0 taken 2311 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2311 times.
✗ Branch 3 not taken.
|
2311 | if (valueBdtypep && valueBdtypep->isSigned()) issigned = true; |
| 2245 | } else { | ||
| 2246 | issigned = bdtypep->isSigned(); | ||
| 2247 | } | ||
| 2248 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2311 times.
|
2311 | if (valueBdtypep->isString()) { |
| 2249 | // parameter X = "str", per IEEE is a number, not a string | ||
| 2250 | if (const auto* const constp = VN_CAST(nodep->valuep(), Const)) { | ||
| 2251 | ✗ | if (constp->num().isString()) { | |
| 2252 | ✗ | width = constp->num().toString().length() * 8; | |
| 2253 | } | ||
| 2254 | } | ||
| 2255 | ✗ | if (width < 8) width = 8; | |
| 2256 | } else if (nodep->valuep()->dtypep()->widthSized()) { | ||
| 2257 | width = nodep->valuep()->width(); | ||
| 2258 | } else { | ||
| 2259 | ✗ | if (nodep->valuep()->width() > 32) { | |
| 2260 | ✗ | nodep->valuep()->v3warn( | |
| 2261 | WIDTH, | ||
| 2262 | "Assigning >32 bit to unranged parameter (defaults to 32 bits)"); | ||
| 2263 | } | ||
| 2264 | width = 32; | ||
| 2265 | } | ||
| 2266 | // Can't just inherit valuep()->dtypep() as mwidth might not equal width | ||
| 2267 |
2/2✓ Branch 0 taken 1112 times.
✓ Branch 1 taken 1199 times.
|
2311 | if (width == 1) { |
| 2268 | // one bit parameter is same as "parameter [0] foo", | ||
| 2269 | // not "parameter logic foo" as you can extract | ||
| 2270 | // "foo[0]" from a parameter but not a wire | ||
| 2271 | 1112 | nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(), | |
| 2272 | VSigning::fromBool(issigned)); | ||
| 2273 | 2224 | nodep->dtypep(nodep->findLogicRangeDType(VNumRange{0, 0}, | |
| 2274 | nodep->valuep()->widthMin(), | ||
| 2275 | VSigning::fromBool(issigned))); | ||
| 2276 | } else { | ||
| 2277 | 1199 | nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(), | |
| 2278 | VSigning::fromBool(issigned)); | ||
| 2279 | } | ||
| 2280 | didchk = true; | ||
| 2281 | } | ||
| 2282 | 2311 | iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, | |
| 2283 | nodep->dtypep()); | ||
| 2284 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 2311 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
2311 | UINFO(9, "implicitParamFromIV " << nodep->valuep() << endl); |
| 2285 | // UINFO below will print variable nodep | ||
| 2286 | } else { | ||
| 2287 | // Or, if nothing assigned, they're integral | ||
| 2288 | ✗ | nodep->dtypeSetSigned32(); | |
| 2289 | VL_DANGLING(bdtypep); | ||
| 2290 | } | ||
| 2291 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 80095 times.
✓ Branch 2 taken 80095 times.
✗ Branch 3 not taken.
|
80095 | } else if (bdtypep && bdtypep->implicit()) { // Implicits get converted to size 1 |
| 2292 | ✗ | nodep->dtypeSetLogicSized(1, bdtypep->numeric()); | |
| 2293 | VL_DANGLING(bdtypep); | ||
| 2294 | } | ||
| 2295 |
4/4✓ Branch 0 taken 4622 times.
✓ Branch 1 taken 77784 times.
✓ Branch 2 taken 2311 times.
✓ Branch 3 taken 2311 times.
|
82406 | if (nodep->valuep() && !didchk) { |
| 2296 | // if (debug()) nodep->dumpTree("- final: "); | ||
| 2297 | // AstPattern requires assignments to pass datatype on PRELIM | ||
| 2298 | 2311 | userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 2299 | 2311 | iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep()); | |
| 2300 | } | ||
| 2301 | 82406 | userIterateAndNext(nodep->delayp(), WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 2302 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 82406 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
82406 | UINFO(4, "varWidthed " << nodep << endl); |
| 2303 | // if (debug()) nodep->dumpTree("- InitOut: "); | ||
| 2304 | nodep->didWidth(true); | ||
| 2305 | nodep->doingWidth(false); | ||
| 2306 | } | ||
| 2307 |
2/2✓ Branch 0 taken 599929 times.
✓ Branch 1 taken 545347 times.
|
1145276 | void visit(AstNodeVarRef* nodep) override { |
| 2308 |
2/2✓ Branch 0 taken 599929 times.
✓ Branch 1 taken 545347 times.
|
1145276 | if (nodep->didWidth()) return; |
| 2309 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 599929 times.
|
599929 | if (!nodep->varp()) { |
| 2310 | ✗ | if (m_paramsOnly && VN_IS(nodep, VarXRef)) { | |
| 2311 | ✗ | checkConstantOrReplace( | |
| 2312 | nodep, "Parameter-resolved constants must not use dotted references: " | ||
| 2313 | ✗ | + nodep->prettyNameQ()); | |
| 2314 | VL_DANGLING(nodep); | ||
| 2315 | ✗ | return; | |
| 2316 | } else { | ||
| 2317 | ✗ | nodep->v3fatalSrc("Unlinked varref"); | |
| 2318 | } | ||
| 2319 | } | ||
| 2320 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 599929 times.
|
599929 | if (!nodep->varp()->didWidth()) { |
| 2321 | // Var hasn't been widthed, so make it so. | ||
| 2322 | userIterate(nodep->varp(), nullptr); | ||
| 2323 | } | ||
| 2324 | // if (debug()>=9) { nodep->dumpTree("- VRin:: "); | ||
| 2325 | // nodep->varp()->dumpTree("- forvar: "); } | ||
| 2326 | // Note genvar's are also entered as integers | ||
| 2327 |
1/2✓ Branch 0 taken 599929 times.
✗ Branch 1 not taken.
|
599929 | nodep->dtypeFrom(nodep->varp()); |
| 2328 |
2/2✓ Branch 0 taken 123115 times.
✓ Branch 1 taken 11011 times.
|
134126 | if (VN_IS(nodep->backp(), NodeAssign) && nodep->access().isWriteOrRW()) { // On LHS |
| 2329 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123115 times.
|
123115 | UASSERT_OBJ(nodep->dtypep(), nodep, "LHS var should be dtype completed"); |
| 2330 | } | ||
| 2331 | // if (debug() >= 9) nodep->dumpTree("- VRout: "); | ||
| 2332 |
3/4✓ Branch 0 taken 125191 times.
✓ Branch 1 taken 474738 times.
✓ Branch 2 taken 125191 times.
✗ Branch 3 not taken.
|
599929 | if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) { |
| 2333 | ✗ | nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ()); | |
| 2334 |
2/2✓ Branch 0 taken 125191 times.
✓ Branch 1 taken 474738 times.
|
599929 | } else if (!nodep->varp()->isForced() && nodep->access().isWriteOrRW() |
| 2335 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 125191 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
125191 | && nodep->varp()->isInput() && !nodep->varp()->isFuncLocal() |
| 2336 | ✗ | && nodep->varp()->isReadOnly() && (!m_ftaskp || !m_ftaskp->isConstructor()) | |
| 2337 | ✗ | && !VN_IS(m_procedurep, InitialAutomatic) | |
| 2338 |
1/2✓ Branch 0 taken 599929 times.
✗ Branch 1 not taken.
|
599929 | && !VN_IS(m_procedurep, InitialStatic)) { |
| 2339 | ✗ | nodep->v3warn(ASSIGNIN, "Assigning to input/const variable: " << nodep->prettyNameQ()); | |
| 2340 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 125191 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
125191 | } else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly |
| 2341 | ✗ | && (!m_ftaskp || !m_ftaskp->isConstructor()) | |
| 2342 | ✗ | && !VN_IS(m_procedurep, InitialAutomatic) | |
| 2343 |
2/2✓ Branch 0 taken 125191 times.
✓ Branch 1 taken 474738 times.
|
599929 | && !VN_IS(m_procedurep, InitialStatic)) { |
| 2344 | // Too loose, but need to allow our generated first assignment | ||
| 2345 | // Move this to a property of the AstInitial block | ||
| 2346 | ✗ | nodep->v3error("Assigning to const variable: " << nodep->prettyNameQ()); | |
| 2347 | } | ||
| 2348 | nodep->didWidth(true); | ||
| 2349 | } | ||
| 2350 | |||
| 2351 | ✗ | void visit(AstEnumDType* nodep) override { | |
| 2352 | ✗ | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | |
| 2353 | ✗ | UINFO(5, " ENUMDTYPE " << nodep << endl); | |
| 2354 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 2355 | nodep->dtypep(nodep); | ||
| 2356 | ✗ | AstBasicDType* basicp = nodep->dtypep()->skipRefp()->basicp(); | |
| 2357 | ✗ | if (!basicp || !basicp->keyword().isIntNumeric()) { | |
| 2358 | ✗ | nodep->v3error( | |
| 2359 | "Enum type must be an integer atom or vector type (IEEE 1800-2023 6.19)"); | ||
| 2360 | ✗ | basicp = nodep->findSigned32DType()->basicp(); | |
| 2361 | nodep->refDTypep(basicp); | ||
| 2362 | } | ||
| 2363 | nodep->widthFromSub(nodep->subDTypep()); | ||
| 2364 | // Assign widths | ||
| 2365 | ✗ | userIterateAndNext(nodep->itemsp(), WidthVP{nodep->dtypep(), BOTH}.p()); | |
| 2366 | // Assign missing values | ||
| 2367 | ✗ | V3Number num(nodep, nodep->width(), 0); | |
| 2368 | ✗ | const V3Number one{nodep, nodep->width(), 1}; | |
| 2369 | std::map<const V3Number, AstEnumItem*> inits; | ||
| 2370 | ✗ | for (AstEnumItem* itemp = nodep->itemsp(); itemp; | |
| 2371 | ✗ | itemp = VN_AS(itemp->nextp(), EnumItem)) { | |
| 2372 | ✗ | if (itemp->valuep()) { | |
| 2373 | ✗ | if (debug() >= 9) { | |
| 2374 | ✗ | UINFO(0, "EnumInit " << itemp << endl); | |
| 2375 | ✗ | itemp->valuep()->dumpTree("- EnumInit: "); | |
| 2376 | } | ||
| 2377 | V3Const::constifyParamsEdit(itemp->valuep()); // itemp may change | ||
| 2378 | ✗ | if (!VN_IS(itemp->valuep(), Const)) { | |
| 2379 | ✗ | itemp->valuep()->v3error("Enum value isn't a constant"); | |
| 2380 | ✗ | itemp->valuep()->unlinkFrBack()->deleteTree(); | |
| 2381 | ✗ | continue; | |
| 2382 | } | ||
| 2383 | // TODO IEEE says assigning sized number that is not same size as enum is illegal | ||
| 2384 | } | ||
| 2385 | ✗ | if (!itemp->valuep()) { | |
| 2386 | ✗ | if (num.isEqZero() && itemp != nodep->itemsp()) { | |
| 2387 | ✗ | itemp->v3error("Enum value illegally wrapped around (IEEE 1800-2023 6.19)"); | |
| 2388 | } | ||
| 2389 | ✗ | if (num.isFourState()) { | |
| 2390 | ✗ | itemp->v3error("Enum value that is unassigned cannot follow value with X/Zs " | |
| 2391 | "(IEEE 1800-2023 6.19)"); | ||
| 2392 | } | ||
| 2393 | ✗ | itemp->valuep(new AstConst{itemp->fileline(), num}); | |
| 2394 | } | ||
| 2395 | |||
| 2396 | ✗ | const AstConst* const constp = VN_AS(itemp->valuep(), Const); | |
| 2397 | ✗ | if (constp->num().isFourState() && basicp->basicp() && !basicp->isFourstate()) { | |
| 2398 | ✗ | itemp->v3error("Enum value with X/Zs cannot be assigned to non-fourstate type " | |
| 2399 | "(IEEE 1800-2023 6.19)"); | ||
| 2400 | } | ||
| 2401 | ✗ | num.opAssign(constp->num()); | |
| 2402 | // Look for duplicates | ||
| 2403 | ✗ | const auto pair = inits.emplace(num, itemp); | |
| 2404 | ✗ | if (!pair.second) { // IEEE says illegal | |
| 2405 | ✗ | const AstNode* const otherp = pair.first->second; | |
| 2406 | ✗ | itemp->v3error("Overlapping enumeration value: " | |
| 2407 | << itemp->prettyNameQ() << '\n' | ||
| 2408 | << itemp->warnContextPrimary() << '\n' | ||
| 2409 | << otherp->warnOther() << "... Location of original declaration\n" | ||
| 2410 | << otherp->warnContextSecondary()); | ||
| 2411 | } | ||
| 2412 | ✗ | num.opAdd(one, constp->num()); | |
| 2413 | } | ||
| 2414 | } | ||
| 2415 | ✗ | void visit(AstEnumItem* nodep) override { | |
| 2416 | ✗ | UINFO(5, " ENUMITEM " << nodep << endl); | |
| 2417 | ✗ | VL_RESTORER(m_enumItemp); | |
| 2418 | ✗ | m_enumItemp = nodep; | |
| 2419 | ✗ | AstNodeDType* const vdtypep = m_vup->dtypep(); | |
| 2420 | ✗ | UASSERT_OBJ(vdtypep, nodep, "ENUMITEM not under ENUM"); | |
| 2421 | ✗ | nodep->dtypep(vdtypep); | |
| 2422 | ✗ | if (nodep->valuep()) { // else the value will be assigned sequentially | |
| 2423 | // Default type is int, but common to assign narrower values, so minwidth from value | ||
| 2424 | ✗ | userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 2425 | bool warnOn = true; | ||
| 2426 | if (const AstConst* const constp = VN_CAST(nodep->valuep(), Const)) { | ||
| 2427 | ✗ | if (static_cast<int>(constp->num().mostSetBitP1()) > nodep->width()) { | |
| 2428 | ✗ | constp->v3error("Enum value exceeds width of enum type (IEEE 1800-2023 6.19)"); | |
| 2429 | warnOn = false; // Prevent normal WIDTHTRUNC | ||
| 2430 | } | ||
| 2431 | } | ||
| 2432 | // Minwidth does not come from value, as spec says set based on parent | ||
| 2433 | // and if we keep minwidth we'll consider it unsized which is incorrect | ||
| 2434 | ✗ | iterateCheck(nodep, "Enum value", nodep->valuep(), CONTEXT_DET, FINAL, nodep->dtypep(), | |
| 2435 | EXTEND_EXP, warnOn); | ||
| 2436 | // Always create a cast, to avoid later ENUMVALUE warnings | ||
| 2437 | nodep->valuep(new AstCast{nodep->valuep()->fileline(), nodep->valuep()->unlinkFrBack(), | ||
| 2438 | ✗ | nodep->dtypep()}); | |
| 2439 | } | ||
| 2440 | } | ||
| 2441 | ✗ | void visit(AstEnumItemRef* nodep) override { | |
| 2442 | ✗ | if (!nodep->itemp()->didWidth()) { | |
| 2443 | // We need to do the whole enum en masse | ||
| 2444 | AstNode* enump = nodep->itemp(); | ||
| 2445 | ✗ | UASSERT_OBJ(enump, nodep, "EnumItemRef not linked"); | |
| 2446 | ✗ | for (; enump; enump = enump->backp()) { | |
| 2447 | if (VN_IS(enump, EnumDType)) break; | ||
| 2448 | } | ||
| 2449 | ✗ | UASSERT_OBJ(enump, nodep, "EnumItemRef can't deref back to an Enum"); | |
| 2450 | ✗ | VL_DO_DANGLING(userIterate(enump, m_vup), enump); // Parent enump maybe relinked | |
| 2451 | } | ||
| 2452 | nodep->dtypeFrom(nodep->itemp()); | ||
| 2453 | } | ||
| 2454 | ✗ | void visit(AstConsAssoc* nodep) override { | |
| 2455 | // Type computed when constructed here | ||
| 2456 | ✗ | auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType); | |
| 2457 | ✗ | UASSERT_OBJ(vdtypep, nodep, "ConsAssoc requires assoc upper parent data type"); | |
| 2458 | ✗ | if (m_vup->prelim()) { | |
| 2459 | nodep->dtypeFrom(vdtypep); | ||
| 2460 | ✗ | if (nodep->defaultp()) { | |
| 2461 | ✗ | iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT_DET, FINAL, | |
| 2462 | vdtypep->subDTypep(), EXTEND_EXP); | ||
| 2463 | } | ||
| 2464 | } | ||
| 2465 | } | ||
| 2466 | ✗ | void visit(AstSetAssoc* nodep) override { | |
| 2467 | // Type computed when constructed here | ||
| 2468 | ✗ | auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType); | |
| 2469 | ✗ | UASSERT_OBJ(vdtypep, nodep, "SetsAssoc requires assoc upper parent data type"); | |
| 2470 | ✗ | if (m_vup->prelim()) { | |
| 2471 | nodep->dtypeFrom(vdtypep); | ||
| 2472 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p()); | |
| 2473 | ✗ | iterateCheck(nodep, "key", nodep->keyp(), CONTEXT_DET, FINAL, vdtypep->keyDTypep(), | |
| 2474 | EXTEND_EXP); | ||
| 2475 | ✗ | iterateCheck(nodep, "value", nodep->valuep(), CONTEXT_DET, FINAL, vdtypep->subDTypep(), | |
| 2476 | EXTEND_EXP); | ||
| 2477 | } | ||
| 2478 | } | ||
| 2479 | ✗ | void visit(AstConsWildcard* nodep) override { | |
| 2480 | // Type computed when constructed here | ||
| 2481 | ✗ | auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType); | |
| 2482 | ✗ | UASSERT_OBJ(vdtypep, nodep, "ConsWildcard requires wildcard upper parent data type"); | |
| 2483 | ✗ | if (m_vup->prelim()) { | |
| 2484 | nodep->dtypeFrom(vdtypep); | ||
| 2485 | ✗ | if (nodep->defaultp()) { | |
| 2486 | ✗ | iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT_DET, FINAL, | |
| 2487 | vdtypep->subDTypep(), EXTEND_EXP); | ||
| 2488 | } | ||
| 2489 | } | ||
| 2490 | } | ||
| 2491 | ✗ | void visit(AstSetWildcard* nodep) override { | |
| 2492 | // Type computed when constructed here | ||
| 2493 | ✗ | auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType); | |
| 2494 | ✗ | UASSERT_OBJ(vdtypep, nodep, "SetWildcard requires wildcard upper parent data type"); | |
| 2495 | ✗ | if (m_vup->prelim()) { | |
| 2496 | nodep->dtypeFrom(vdtypep); | ||
| 2497 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p()); | |
| 2498 | ✗ | iterateCheck(nodep, "key", nodep->keyp(), CONTEXT_DET, FINAL, | |
| 2499 | vdtypep->findStringDType(), EXTEND_EXP); | ||
| 2500 | ✗ | iterateCheck(nodep, "value", nodep->valuep(), CONTEXT_DET, FINAL, vdtypep->subDTypep(), | |
| 2501 | EXTEND_EXP); | ||
| 2502 | } | ||
| 2503 | } | ||
| 2504 | ✗ | void visit(AstConsPackUOrStruct* nodep) override { | |
| 2505 | // Type was computed when constructed in V3Width earlier | ||
| 2506 | ✗ | auto* const vdtypep = VN_AS(nodep->dtypep()->skipRefp(), NodeUOrStructDType); | |
| 2507 | ✗ | UASSERT_OBJ(vdtypep, nodep, "ConsPackUOrStruct requires packed array parent data type"); | |
| 2508 | ✗ | userIterateChildren(nodep, WidthVP{vdtypep, BOTH}.p()); | |
| 2509 | } | ||
| 2510 | ✗ | void visit(AstConsPackMember* nodep) override { | |
| 2511 | // Type was computed when constructed in V3Width earlier | ||
| 2512 | ✗ | auto* const vdtypep = VN_AS(nodep->dtypep(), MemberDType); | |
| 2513 | ✗ | UASSERT_OBJ(vdtypep, nodep, "ConsPackMember requires member data type"); | |
| 2514 | ✗ | if (m_vup->prelim()) userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, BOTH}.p()); | |
| 2515 | } | ||
| 2516 | ✗ | void visit(AstConsDynArray* nodep) override { | |
| 2517 | // Type computed when constructed here | ||
| 2518 | ✗ | AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType); | |
| 2519 | ✗ | UASSERT_OBJ(vdtypep, nodep, "ConsDynArray requires queue upper parent data type"); | |
| 2520 | ✗ | if (m_vup->prelim()) { | |
| 2521 | ✗ | AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2522 | ✗ | AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2523 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p()); | |
| 2524 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p()); | |
| 2525 | nodep->dtypeFrom(vdtypep); | ||
| 2526 | } | ||
| 2527 | ✗ | if (m_vup->final()) { | |
| 2528 | if (nodep->didWidthAndSet()) return; | ||
| 2529 | // Arguments can be either elements of the queue or a queue itself | ||
| 2530 | // Concats (part of tree of concats) must always become ConsDynArray's | ||
| 2531 | ✗ | AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2532 | ✗ | AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2533 | ✗ | if (nodep->lhsp()) { | |
| 2534 | ✗ | if (nodep->lhsIsValue()) { | |
| 2535 | // Sub elements are not queues, but concats, must always pass concats down | ||
| 2536 | ✗ | iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL); | |
| 2537 | } else { | ||
| 2538 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p()); | |
| 2539 | } | ||
| 2540 | } | ||
| 2541 | ✗ | if (nodep->rhsp()) { | |
| 2542 | ✗ | if (nodep->rhsIsValue()) { | |
| 2543 | ✗ | iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL); | |
| 2544 | } else { | ||
| 2545 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p()); | |
| 2546 | } | ||
| 2547 | } | ||
| 2548 | if (nodep->didWidthAndSet()) return; | ||
| 2549 | nodep->dtypeFrom(vdtypep); | ||
| 2550 | } | ||
| 2551 | } | ||
| 2552 | ✗ | void visit(AstConsQueue* nodep) override { | |
| 2553 | // Type computed when constructed here | ||
| 2554 | ✗ | AstQueueDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), QueueDType); | |
| 2555 | ✗ | UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type"); | |
| 2556 | ✗ | if (m_vup->prelim()) { | |
| 2557 | ✗ | AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2558 | ✗ | AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2559 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p()); | |
| 2560 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p()); | |
| 2561 | nodep->dtypeFrom(vdtypep); | ||
| 2562 | } | ||
| 2563 | ✗ | if (m_vup->final()) { | |
| 2564 | if (nodep->didWidthAndSet()) return; | ||
| 2565 | // Arguments can be either elements of the queue or a queue itself | ||
| 2566 | // Concats (part of tree of concats) must always become ConsQueue's | ||
| 2567 | ✗ | AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2568 | ✗ | AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep; | |
| 2569 | ✗ | if (nodep->lhsp()) { | |
| 2570 | ✗ | if (nodep->lhsIsValue()) { | |
| 2571 | // Sub elements are not queues, but concats, must always pass concats down | ||
| 2572 | ✗ | iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL); | |
| 2573 | } else { | ||
| 2574 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p()); | |
| 2575 | } | ||
| 2576 | } | ||
| 2577 | ✗ | if (nodep->rhsp()) { | |
| 2578 | ✗ | if (nodep->rhsIsValue()) { | |
| 2579 | ✗ | iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL); | |
| 2580 | } else { | ||
| 2581 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p()); | |
| 2582 | } | ||
| 2583 | } | ||
| 2584 | nodep->dtypeFrom(vdtypep); | ||
| 2585 | } | ||
| 2586 | } | ||
| 2587 | ✗ | void visit(AstInitItem* nodep) override { // | |
| 2588 | ✗ | userIterateChildren(nodep, m_vup); | |
| 2589 | } | ||
| 2590 | ✗ | void visit(AstInitArray* nodep) override { | |
| 2591 | // InitArray has type of the array; children are array values | ||
| 2592 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 2593 | AstNodeDType* const vdtypep = m_vup->dtypeNullp(); | ||
| 2594 | ✗ | UASSERT_OBJ(vdtypep, nodep, "InitArray type not assigned by AstPattern/Var visitor"); | |
| 2595 | ✗ | nodep->dtypep(vdtypep); | |
| 2596 | const AstNodeDType* const arrayp = vdtypep->skipRefp(); | ||
| 2597 | if (VN_IS(arrayp, NodeArrayDType) || VN_IS(arrayp, AssocArrayDType)) { | ||
| 2598 | ✗ | userIterateChildren(nodep, WidthVP{arrayp->subDTypep(), BOTH}.p()); | |
| 2599 | } else { | ||
| 2600 | ✗ | UINFO(1, "on " << nodep << endl); | |
| 2601 | ✗ | UINFO(1, "dtype object " << arrayp << endl); | |
| 2602 | ✗ | nodep->v3fatalSrc("InitArray on non-array"); | |
| 2603 | } | ||
| 2604 | } | ||
| 2605 | } | ||
| 2606 | ✗ | void visit(AstDist* nodep) override { | |
| 2607 | // x dist {a :/ p, b :/ q} --> (p > 0 && x == a) || (q > 0 && x == b) | ||
| 2608 | ✗ | nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)"); | |
| 2609 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 2610 | ✗ | for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { | |
| 2611 | nextip = itemp->nextp(); // iterate may cause the node to get replaced | ||
| 2612 | ✗ | VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp); | |
| 2613 | } | ||
| 2614 | |||
| 2615 | AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType); | ||
| 2616 | AstNodeDType* subDTypep = nullptr; | ||
| 2617 | ✗ | nodep->dtypeSetBit(); | |
| 2618 | |||
| 2619 | ✗ | if (dtype && dtype->isString()) { | |
| 2620 | ✗ | nodep->dtypeSetString(); | |
| 2621 | subDTypep = nodep->findStringDType(); | ||
| 2622 | ✗ | } else if (dtype && dtype->isDouble()) { | |
| 2623 | ✗ | nodep->dtypeSetDouble(); | |
| 2624 | subDTypep = nodep->findDoubleDType(); | ||
| 2625 | } else { | ||
| 2626 | // Take width as maximum across all items | ||
| 2627 | ✗ | int width = nodep->exprp()->width(); | |
| 2628 | ✗ | int mwidth = nodep->exprp()->widthMin(); | |
| 2629 | ✗ | for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { | |
| 2630 | ✗ | width = std::max(width, itemp->width()); | |
| 2631 | ✗ | mwidth = std::max(mwidth, itemp->widthMin()); | |
| 2632 | } | ||
| 2633 | ✗ | subDTypep = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric()); | |
| 2634 | } | ||
| 2635 | |||
| 2636 | ✗ | iterateCheck(nodep, "Dist expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep, | |
| 2637 | EXTEND_EXP); | ||
| 2638 | ✗ | for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { | |
| 2639 | nextip = itemp->nextp(); | ||
| 2640 | ✗ | itemp = VN_AS(itemp, DistItem)->rangep(); | |
| 2641 | // InsideRange will get replaced with Lte&Gte and finalized later | ||
| 2642 | if (!VN_IS(itemp, InsideRange)) | ||
| 2643 | ✗ | iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); | |
| 2644 | } | ||
| 2645 | AstNodeExpr* newp = nullptr; | ||
| 2646 | ✗ | for (AstDistItem* itemp = nodep->itemsp(); itemp; | |
| 2647 | ✗ | itemp = VN_AS(itemp->nextp(), DistItem)) { | |
| 2648 | ✗ | AstNodeExpr* inewp = insideItem(nodep, nodep->exprp(), itemp->rangep()); | |
| 2649 | ✗ | if (!inewp) continue; | |
| 2650 | AstNodeExpr* const cmpp | ||
| 2651 | = new AstGt{itemp->fileline(), itemp->weightp()->unlinkFrBack(), | ||
| 2652 | ✗ | new AstConst{itemp->fileline(), 0}}; | |
| 2653 | cmpp->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true); | ||
| 2654 | cmpp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true); | ||
| 2655 | ✗ | inewp = new AstLogAnd{itemp->fileline(), cmpp, inewp}; | |
| 2656 | ✗ | newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp; | |
| 2657 | } | ||
| 2658 | ✗ | if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; | |
| 2659 | |||
| 2660 | ✗ | if (debug() >= 9) nodep->dumpTree("- dist-out: "); | |
| 2661 | ✗ | nodep->replaceWith(newp); | |
| 2662 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2663 | } | ||
| 2664 | |||
| 2665 | ✗ | void visit(AstInside* nodep) override { | |
| 2666 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 2667 | ✗ | for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { | |
| 2668 | nextip = itemp->nextp(); // iterate may cause the node to get replaced | ||
| 2669 | ✗ | VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp); | |
| 2670 | } | ||
| 2671 | |||
| 2672 | AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType); | ||
| 2673 | AstNodeDType* expDTypep = nullptr; | ||
| 2674 | |||
| 2675 | ✗ | if (dtype && dtype->isString()) { | |
| 2676 | ✗ | nodep->dtypeSetString(); | |
| 2677 | expDTypep = nodep->findStringDType(); | ||
| 2678 | ✗ | } else if (dtype && dtype->isDouble()) { | |
| 2679 | ✗ | nodep->dtypeSetDouble(); | |
| 2680 | expDTypep = nodep->findDoubleDType(); | ||
| 2681 | } else { | ||
| 2682 | // Take width as maximum across all items | ||
| 2683 | ✗ | int width = nodep->exprp()->width(); | |
| 2684 | ✗ | int mwidth = nodep->exprp()->widthMin(); | |
| 2685 | ✗ | bool isFourstate = nodep->exprp()->dtypep()->isFourstate(); | |
| 2686 | ✗ | for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { | |
| 2687 | ✗ | width = std::max(width, itemp->width()); | |
| 2688 | ✗ | mwidth = std::max(mwidth, itemp->widthMin()); | |
| 2689 | ✗ | isFourstate |= itemp->dtypep()->isFourstate(); | |
| 2690 | } | ||
| 2691 | ✗ | nodep->dtypeSetBit(); | |
| 2692 | const VSigning numeric = nodep->exprp()->dtypep()->numeric(); | ||
| 2693 | ✗ | expDTypep = isFourstate ? nodep->findLogicDType(width, mwidth, numeric) | |
| 2694 | ✗ | : nodep->findBitDType(width, mwidth, numeric); | |
| 2695 | } | ||
| 2696 | |||
| 2697 | ✗ | iterateCheck(nodep, "Inside expression", nodep->exprp(), CONTEXT_DET, FINAL, expDTypep, | |
| 2698 | EXTEND_EXP); | ||
| 2699 | ✗ | for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { | |
| 2700 | nextip = itemp->nextp(); // iterate may cause the node to get replaced | ||
| 2701 | // InsideRange will get replaced with Lte&Gte and finalized later | ||
| 2702 | if (!VN_IS(itemp, InsideRange)) | ||
| 2703 | ✗ | iterateCheck(nodep, "Inside Item", itemp, CONTEXT_DET, FINAL, expDTypep, | |
| 2704 | EXTEND_EXP); | ||
| 2705 | } | ||
| 2706 | |||
| 2707 | ✗ | if (debug() >= 9) nodep->dumpTree("- inside-in: "); | |
| 2708 | // Now rip out the inside and replace with simple math | ||
| 2709 | AstNodeExpr* newp = nullptr; | ||
| 2710 | ✗ | for (AstNodeExpr *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { | |
| 2711 | ✗ | nextip = VN_AS(itemp->nextp(), NodeExpr); // Will be unlinking | |
| 2712 | ✗ | AstNodeExpr* const inewp = insideItem(nodep, nodep->exprp(), itemp); | |
| 2713 | ✗ | if (!inewp) continue; | |
| 2714 | ✗ | newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp; | |
| 2715 | } | ||
| 2716 | ✗ | if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; | |
| 2717 | ✗ | if (debug() >= 9) newp->dumpTree("- inside-out: "); | |
| 2718 | ✗ | nodep->replaceWith(newp); | |
| 2719 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2720 | } | ||
| 2721 | ✗ | AstNodeExpr* insideItem(AstNode* nodep, AstNodeExpr* exprp, AstNodeExpr* itemp) { | |
| 2722 | const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp(); | ||
| 2723 | if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) { | ||
| 2724 | // Similar logic in V3Case | ||
| 2725 | ✗ | return irangep->newAndFromInside(exprp, irangep->lhsp()->unlinkFrBack(), | |
| 2726 | ✗ | irangep->rhsp()->unlinkFrBack()); | |
| 2727 | } else if (VN_IS(itemDtp, UnpackArrayDType) || VN_IS(itemDtp, DynArrayDType) | ||
| 2728 | || VN_IS(itemDtp, QueueDType)) { | ||
| 2729 | // Unsupported in parameters | ||
| 2730 | AstNodeExpr* const cexprp = exprp->cloneTreePure(true); | ||
| 2731 | AstNodeExpr* const inewp | ||
| 2732 | ✗ | = new AstCMethodHard{nodep->fileline(), itemp->unlinkFrBack(), "inside", cexprp}; | |
| 2733 | ✗ | iterateCheckTyped(nodep, "inside value", cexprp, itemDtp->subDTypep(), BOTH); | |
| 2734 | VL_DANGLING(cexprp); // Might have been replaced | ||
| 2735 | ✗ | inewp->dtypeSetBit(); | |
| 2736 | inewp->didWidth(true); | ||
| 2737 | return inewp; | ||
| 2738 | } else if (VN_IS(itemDtp, AssocArrayDType)) { | ||
| 2739 | ✗ | nodep->v3error("Inside operator not specified on associative arrays " | |
| 2740 | "(IEEE 1800-2023 11.4.13)"); | ||
| 2741 | ✗ | return nullptr; | |
| 2742 | } | ||
| 2743 | ✗ | return AstEqWild::newTyped(itemp->fileline(), exprp->cloneTreePure(true), | |
| 2744 | ✗ | itemp->unlinkFrBack()); | |
| 2745 | } | ||
| 2746 | ✗ | void visit(AstInsideRange* nodep) override { | |
| 2747 | // Just do each side; AstInside will rip these nodes out later | ||
| 2748 | ✗ | userIterateAndNext(nodep->lhsp(), m_vup); | |
| 2749 | ✗ | userIterateAndNext(nodep->rhsp(), m_vup); | |
| 2750 | nodep->dtypeFrom(nodep->lhsp()); | ||
| 2751 | } | ||
| 2752 | |||
| 2753 | ✗ | void visit(AstIfaceRefDType* nodep) override { | |
| 2754 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 2755 | ✗ | UINFO(5, " IFACEREF " << nodep << endl); | |
| 2756 | ✗ | userIterateChildren(nodep, m_vup); | |
| 2757 | nodep->dtypep(nodep); | ||
| 2758 | ✗ | UINFO(4, "dtWidthed " << nodep << endl); | |
| 2759 | } | ||
| 2760 | ✗ | void visit(AstNodeUOrStructDType* nodep) override { | |
| 2761 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 2762 | ✗ | UINFO(5, " NODEUORS " << nodep << endl); | |
| 2763 | // if (debug() >= 9) nodep->dumpTree("- class-in: "); | ||
| 2764 | ✗ | if (!nodep->packed() && v3Global.opt.structsPacked()) nodep->packed(true); | |
| 2765 | userIterateChildren(nodep, nullptr); // First size all members | ||
| 2766 | nodep->dtypep(nodep); | ||
| 2767 | nodep->isFourstate(false); | ||
| 2768 | // Error checks | ||
| 2769 | ✗ | for (AstMemberDType* itemp = nodep->membersp(); itemp; | |
| 2770 | ✗ | itemp = VN_AS(itemp->nextp(), MemberDType)) { | |
| 2771 | AstNodeDType* const dtp = itemp->subDTypep()->skipRefp(); | ||
| 2772 | if (nodep->packed() | ||
| 2773 | ✗ | && !dtp->isIntegralOrPacked() | |
| 2774 | // Historically lax: | ||
| 2775 | ✗ | && !v3Global.opt.structsPacked()) | |
| 2776 | ✗ | itemp->v3error("Unpacked data type " | |
| 2777 | << dtp->prettyDTypeNameQ() | ||
| 2778 | << " in packed struct/union (IEEE 1800-2023 7.2.1)"); | ||
| 2779 | ✗ | if ((VN_IS(nodep, UnionDType) || nodep->packed()) && itemp->valuep()) { | |
| 2780 | ✗ | itemp->v3error("Initial values not allowed in packed struct/union" | |
| 2781 | " (IEEE 1800-2023 7.2.2)"); | ||
| 2782 | ✗ | pushDeletep(itemp->valuep()->unlinkFrBack()); | |
| 2783 | ✗ | } else if (itemp->valuep()) { | |
| 2784 | ✗ | itemp->valuep()->v3warn(E_UNSUPPORTED, | |
| 2785 | "Unsupported: Initial values in struct/union members"); | ||
| 2786 | ✗ | pushDeletep(itemp->valuep()->unlinkFrBack()); | |
| 2787 | } | ||
| 2788 | } | ||
| 2789 | // Determine bit assignments and width | ||
| 2790 | ✗ | if (VN_IS(nodep, UnionDType) || nodep->packed()) { | |
| 2791 | int lsb = 0; | ||
| 2792 | ✗ | int width = 0; | |
| 2793 | // Report errors on first member first | ||
| 2794 | AstMemberDType* itemp; | ||
| 2795 | // MSB is first, so loop backwards | ||
| 2796 | ✗ | for (itemp = nodep->membersp(); itemp && itemp->nextp(); | |
| 2797 | ✗ | itemp = VN_AS(itemp->nextp(), MemberDType)) {} | |
| 2798 | ✗ | for (; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) { | |
| 2799 | ✗ | if (itemp->isFourstate()) nodep->isFourstate(true); | |
| 2800 | itemp->lsb(lsb); | ||
| 2801 | if (VN_IS(nodep, UnionDType)) { | ||
| 2802 | ✗ | width = std::max(width, itemp->width()); | |
| 2803 | } else { | ||
| 2804 | ✗ | lsb += itemp->width(); | |
| 2805 | ✗ | width += itemp->width(); | |
| 2806 | } | ||
| 2807 | } | ||
| 2808 | ✗ | nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration | |
| 2809 | } else { | ||
| 2810 | nodep->widthForce(1, 1); | ||
| 2811 | } | ||
| 2812 | // if (debug() >= 9) nodep->dumpTree("- class-out: "); | ||
| 2813 | } | ||
| 2814 | ✗ | void visit(AstClass* nodep) override { | |
| 2815 | if (nodep->didWidthAndSet()) return; | ||
| 2816 | |||
| 2817 | // If the class is std::process | ||
| 2818 | ✗ | if (nodep->name() == "process") { | |
| 2819 | AstPackage* const packagep = getItemPackage(nodep); | ||
| 2820 | ✗ | if (packagep && packagep->name() == "std") { | |
| 2821 | // Change type of m_process to VlProcessRef | ||
| 2822 | ✗ | if (AstVar* const varp | |
| 2823 | ✗ | = VN_CAST(m_memberMap.findMember(nodep, "m_process"), Var)) { | |
| 2824 | AstNodeDType* const dtypep = varp->getChildDTypep(); | ||
| 2825 | ✗ | if (!varp->dtypep()) { | |
| 2826 | VL_DO_DANGLING(pushDeletep(dtypep->unlinkFrBack()), dtypep); | ||
| 2827 | } | ||
| 2828 | AstBasicDType* const newdtypep = new AstBasicDType{ | ||
| 2829 | ✗ | nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED}; | |
| 2830 | v3Global.rootp()->typeTablep()->addTypesp(newdtypep); | ||
| 2831 | varp->dtypep(newdtypep); | ||
| 2832 | } | ||
| 2833 | // Mark that self requires process instance | ||
| 2834 | ✗ | if (AstNodeFTask* const ftaskp | |
| 2835 | ✗ | = VN_CAST(m_memberMap.findMember(nodep, "self"), NodeFTask)) { | |
| 2836 | ftaskp->setNeedProcess(); | ||
| 2837 | } | ||
| 2838 | } | ||
| 2839 | } | ||
| 2840 | |||
| 2841 | // Must do extends first, as we may in functions under this class | ||
| 2842 | // start following a tree of extends that takes us to other classes | ||
| 2843 | ✗ | userIterateAndNext(nodep->extendsp(), nullptr); | |
| 2844 | userIterateChildren(nodep, nullptr); // First size all members | ||
| 2845 | } | ||
| 2846 | ✗ | void visit(AstThisRef* nodep) override { | |
| 2847 | if (nodep->didWidthAndSet()) return; | ||
| 2848 | ✗ | nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep())); | |
| 2849 | } | ||
| 2850 | ✗ | void visit(AstClassRefDType* nodep) override { | |
| 2851 | if (nodep->didWidthAndSet()) return; | ||
| 2852 | // TODO this maybe eventually required to properly resolve members, | ||
| 2853 | // though causes problems with t_class_forward.v, so for now avoided | ||
| 2854 | // userIterateChildren(nodep->classp(), nullptr); | ||
| 2855 | } | ||
| 2856 | ✗ | void visit(AstClassOrPackageRef* nodep) override { | |
| 2857 | if (nodep->didWidthAndSet()) return; | ||
| 2858 | userIterateChildren(nodep, nullptr); | ||
| 2859 | } | ||
| 2860 | ✗ | void visit(AstDot* nodep) override { | |
| 2861 | // We can only reach this from constify called during V3Param (so before linkDotParam) | ||
| 2862 | // ... #(Cls#(...)::...) ... | ||
| 2863 | // ^^~~~ this is our DOT | ||
| 2864 | ✗ | nodep->v3warn(E_UNSUPPORTED, "dotted expressions in parameters\n" | |
| 2865 | << nodep->warnMore() << "... Suggest use a typedef"); | ||
| 2866 | } | ||
| 2867 | ✗ | void visit(AstClassExtends* nodep) override { | |
| 2868 | if (nodep->didWidthAndSet()) return; | ||
| 2869 | if (VN_IS(nodep->childDTypep(), ClassRefDType)) { | ||
| 2870 | ✗ | nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep())); | |
| 2871 | } | ||
| 2872 | } | ||
| 2873 | ✗ | void visit(AstMemberDType* nodep) override { | |
| 2874 | if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed | ||
| 2875 | // Iterate into subDTypep() to resolve that type and update pointer. | ||
| 2876 | ✗ | nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 2877 | nodep->widthFromSub(nodep->subDTypep()); | ||
| 2878 | nodep->dtypeFrom(nodep->subDTypep()); | ||
| 2879 | ✗ | if (nodep->valuep()) { | |
| 2880 | ✗ | userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 2881 | ✗ | iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep()); | |
| 2882 | } | ||
| 2883 | } | ||
| 2884 | ✗ | void visit(AstStructSel* nodep) override { | |
| 2885 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 2886 | } | ||
| 2887 | ✗ | void visit(AstMemberSel* nodep) override { | |
| 2888 | ✗ | UINFO(5, " MEMBERSEL " << nodep << endl); | |
| 2889 | ✗ | if (nodep->didWidth()) return; | |
| 2890 | ✗ | if (debug() >= 9) nodep->dumpTree("- mbs-in: "); | |
| 2891 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 2892 | ✗ | if (debug() >= 9) nodep->dumpTree("- mbs-ic: "); | |
| 2893 | // Find the fromp dtype - should be a class | ||
| 2894 | ✗ | UASSERT_OBJ(nodep->fromp()->dtypep(), nodep->fromp(), "Unlinked data type"); | |
| 2895 | AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); | ||
| 2896 | ✗ | UINFO(9, " from dt " << fromDtp << endl); | |
| 2897 | const AstMemberSel* const fromSel = VN_CAST(nodep->fromp(), MemberSel); | ||
| 2898 | ✗ | if (AstClocking* const clockingp = fromSel && fromSel->varp() | |
| 2899 | ✗ | ? VN_CAST(fromSel->varp()->firstAbovep(), Clocking) | |
| 2900 | : nullptr) { | ||
| 2901 | // In: MEMBERSEL{MEMBERSEL{vifaceref, "cb", cb_event}, "clockvar", null} | ||
| 2902 | // Out: MEMBERSEL{vifaceref, "clockvar", clockvar} | ||
| 2903 | ✗ | UINFO(9, " from clocking " << clockingp << endl); | |
| 2904 | ✗ | if (AstVar* const varp = memberSelClocking(nodep, clockingp)) { | |
| 2905 | ✗ | if (!varp->didWidth()) userIterate(varp, nullptr); | |
| 2906 | ✗ | AstMemberSel* fromp = VN_AS(nodep->fromp(), MemberSel); | |
| 2907 | ✗ | fromp->replaceWith(fromp->fromp()->unlinkFrBack()); | |
| 2908 | ✗ | VL_DO_DANGLING(fromp->deleteTree(), fromp); | |
| 2909 | nodep->dtypep(varp->dtypep()); | ||
| 2910 | nodep->varp(varp); | ||
| 2911 | ✗ | if (nodep->access().isWriteOrRW()) V3LinkLValue::linkLValueSet(nodep); | |
| 2912 | if (AstIfaceRefDType* const adtypep | ||
| 2913 | = VN_CAST(nodep->fromp()->dtypep(), IfaceRefDType)) { | ||
| 2914 | nodep->varp()->sensIfacep(adtypep->ifacep()); | ||
| 2915 | } | ||
| 2916 | ✗ | UINFO(9, " done clocking msel " << nodep << endl); | |
| 2917 | nodep->didWidth(true); // Must not visit again: will confuse scopes | ||
| 2918 | return; | ||
| 2919 | } | ||
| 2920 | } else if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) { | ||
| 2921 | ✗ | if (memberSelStruct(nodep, adtypep)) return; | |
| 2922 | } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) { | ||
| 2923 | ✗ | if (memberSelClass(nodep, adtypep)) return; | |
| 2924 | } else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) { | ||
| 2925 | ✗ | if (AstNode* foundp = memberSelIface(nodep, adtypep)) { | |
| 2926 | if (AstClocking* const clockingp = VN_CAST(foundp, Clocking)) | ||
| 2927 | ✗ | foundp = clockingp->ensureEventp(); | |
| 2928 | if (AstVar* const varp = VN_CAST(foundp, Var)) { | ||
| 2929 | ✗ | if (!varp->didWidth()) userIterate(varp, nullptr); | |
| 2930 | nodep->dtypep(foundp->dtypep()); | ||
| 2931 | nodep->varp(varp); | ||
| 2932 | AstIface* const ifacep = adtypep->ifacep(); | ||
| 2933 | varp->sensIfacep(ifacep); | ||
| 2934 | ✗ | nodep->fromp()->foreach( | |
| 2935 | ✗ | [ifacep](AstVarRef* const refp) { refp->varp()->sensIfacep(ifacep); }); | |
| 2936 | nodep->didWidth(true); | ||
| 2937 | ✗ | return; | |
| 2938 | } | ||
| 2939 | ✗ | UINFO(1, "found object " << foundp << endl); | |
| 2940 | ✗ | nodep->v3fatalSrc("MemberSel of non-variable\n" | |
| 2941 | << nodep->warnContextPrimary() << '\n' | ||
| 2942 | << foundp->warnOther() << "... Location of found object\n" | ||
| 2943 | << foundp->warnContextSecondary()); | ||
| 2944 | } | ||
| 2945 | } else if (VN_IS(fromDtp, EnumDType) // | ||
| 2946 | || VN_IS(fromDtp, AssocArrayDType) // | ||
| 2947 | || VN_IS(fromDtp, WildcardArrayDType) // | ||
| 2948 | || VN_IS(fromDtp, UnpackArrayDType) // | ||
| 2949 | || VN_IS(fromDtp, DynArrayDType) // | ||
| 2950 | || VN_IS(fromDtp, QueueDType) // | ||
| 2951 | || VN_IS(fromDtp, ConstraintRefDType) // | ||
| 2952 | || VN_IS(fromDtp, BasicDType)) { | ||
| 2953 | // Method call on enum without following parenthesis, e.g. "ENUM.next" | ||
| 2954 | // Convert this into a method call, and let that visitor figure out what to do next | ||
| 2955 | AstNode* const newp = new AstMethodCall{ | ||
| 2956 | ✗ | nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), nullptr}; | |
| 2957 | ✗ | nodep->replaceWith(newp); | |
| 2958 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2959 | ✗ | userIterate(newp, m_vup); | |
| 2960 | ✗ | return; | |
| 2961 | } else { | ||
| 2962 | ✗ | nodep->v3error("Member selection of non-struct/union object '" | |
| 2963 | << nodep->fromp()->prettyTypeName() << "' which is a '" | ||
| 2964 | << nodep->fromp()->dtypep()->prettyTypeName() << "'"); | ||
| 2965 | } | ||
| 2966 | // Error handling | ||
| 2967 | ✗ | nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); | |
| 2968 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2969 | } | ||
| 2970 | ✗ | bool memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) { | |
| 2971 | ✗ | if (nodep->name() == "rand_mode") { | |
| 2972 | ✗ | nodep->replaceWith(new AstMethodCall{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | |
| 2973 | ✗ | "rand_mode", nullptr}); | |
| 2974 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2975 | ✗ | return true; | |
| 2976 | } | ||
| 2977 | // Returns true if ok | ||
| 2978 | // No need to width-resolve the class, as it was done when we did the child | ||
| 2979 | AstClass* const first_classp = adtypep->classp(); | ||
| 2980 | ✗ | UASSERT_OBJ(first_classp, nodep, "Unlinked"); | |
| 2981 | ✗ | for (AstClass* classp = first_classp; classp;) { | |
| 2982 | ✗ | if (AstNode* const foundp = m_memberMap.findMember(classp, nodep->name())) { | |
| 2983 | if (AstVar* const varp = VN_CAST(foundp, Var)) { | ||
| 2984 | ✗ | if (!varp->didWidth()) userIterate(varp, nullptr); | |
| 2985 | ✗ | if (varp->lifetime().isStatic() || varp->isParam()) { | |
| 2986 | // Static members are moved outside the class, so they shouldn't be | ||
| 2987 | // accessed by member select on a class object | ||
| 2988 | AstVarRef* const varRefp | ||
| 2989 | ✗ | = new AstVarRef{nodep->fileline(), varp, nodep->access()}; | |
| 2990 | varRefp->classOrPackagep(classp); | ||
| 2991 | ✗ | nodep->replaceWith(varRefp); | |
| 2992 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 2993 | ✗ | return true; | |
| 2994 | } | ||
| 2995 | nodep->dtypep(foundp->dtypep()); | ||
| 2996 | nodep->varp(varp); | ||
| 2997 | nodep->didWidth(true); | ||
| 2998 | ✗ | if (nodep->fromp()->sameTree(m_randomizeFromp) && varp->isRand()) // null-safe | |
| 2999 | ✗ | V3LinkLValue::linkLValueSet(nodep); | |
| 3000 | ✗ | return true; | |
| 3001 | } | ||
| 3002 | if (AstConstraint* constrp = VN_CAST(foundp, Constraint)) { | ||
| 3003 | ✗ | nodep->replaceWith(new AstConstraintRef{ | |
| 3004 | ✗ | nodep->fileline(), nodep->fromp()->unlinkFrBack(), constrp}); | |
| 3005 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 3006 | ✗ | return true; | |
| 3007 | } | ||
| 3008 | if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) { | ||
| 3009 | ✗ | nodep->replaceWith(adfoundp->cloneTree(false)); | |
| 3010 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 3011 | ✗ | return true; | |
| 3012 | } | ||
| 3013 | if (VN_IS(foundp, NodeFTask)) { | ||
| 3014 | ✗ | nodep->replaceWith(new AstMethodCall{nodep->fileline(), | |
| 3015 | nodep->fromp()->unlinkFrBack(), | ||
| 3016 | ✗ | nodep->name(), nullptr}); | |
| 3017 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 3018 | ✗ | return true; | |
| 3019 | } | ||
| 3020 | if (VN_IS(foundp, Constraint)) { | ||
| 3021 | // We don't support constraints yet, so just keep as unlinked for now | ||
| 3022 | // Presumably we'll next see a constraint_mode AstMethodCall | ||
| 3023 | ✗ | nodep->dtypep(nodep->findConstraintRefDType()); | |
| 3024 | ✗ | UINFO(9, "Unsupported constraint select " << nodep << endl); | |
| 3025 | ✗ | return true; | |
| 3026 | } | ||
| 3027 | ✗ | UINFO(1, "found object " << foundp << endl); | |
| 3028 | ✗ | nodep->v3fatalSrc("MemberSel of non-variable\n" | |
| 3029 | << nodep->warnContextPrimary() << '\n' | ||
| 3030 | << foundp->warnOther() << "... Location of found object\n" | ||
| 3031 | << foundp->warnContextSecondary()); | ||
| 3032 | } | ||
| 3033 | ✗ | classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; | |
| 3034 | } | ||
| 3035 | |||
| 3036 | VSpellCheck speller; | ||
| 3037 | ✗ | for (AstClass* classp = first_classp; classp;) { | |
| 3038 | ✗ | for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) { | |
| 3039 | if (VN_IS(itemp, Constraint) || VN_IS(itemp, EnumItemRef) || VN_IS(itemp, Var)) { | ||
| 3040 | ✗ | speller.pushCandidate(itemp->prettyName()); | |
| 3041 | } | ||
| 3042 | } | ||
| 3043 | ✗ | classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; | |
| 3044 | } | ||
| 3045 | ✗ | const string suggest = speller.bestCandidateMsg(nodep->prettyName()); | |
| 3046 | ✗ | nodep->v3error( | |
| 3047 | "Member " << nodep->prettyNameQ() << " not found in class " | ||
| 3048 | << first_classp->prettyNameQ() << "\n" | ||
| 3049 | << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest)); | ||
| 3050 | return false; // Caller handles error | ||
| 3051 | } | ||
| 3052 | ✗ | AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) { | |
| 3053 | // Returns node if ok | ||
| 3054 | // No need to width-resolve the interface, as it was done when we did the child | ||
| 3055 | AstNodeModule* const ifacep = adtypep->ifacep(); | ||
| 3056 | ✗ | UASSERT_OBJ(ifacep, nodep, "Unlinked"); | |
| 3057 | VSpellCheck speller; | ||
| 3058 | ✗ | for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) { | |
| 3059 | ✗ | if (itemp->name() == nodep->name()) return itemp; | |
| 3060 | if (VN_IS(itemp, Var) || VN_IS(itemp, Modport)) { | ||
| 3061 | ✗ | speller.pushCandidate(itemp->prettyName()); | |
| 3062 | } | ||
| 3063 | } | ||
| 3064 | ✗ | const string suggest = speller.bestCandidateMsg(nodep->prettyName()); | |
| 3065 | ✗ | nodep->v3error( | |
| 3066 | "Member " << nodep->prettyNameQ() << " not found in interface " | ||
| 3067 | << ifacep->prettyNameQ() << "\n" | ||
| 3068 | << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest)); | ||
| 3069 | return nullptr; // Caller handles error | ||
| 3070 | } | ||
| 3071 | ✗ | AstVar* memberSelClocking(AstMemberSel* nodep, AstClocking* clockingp) { | |
| 3072 | // Returns node if ok | ||
| 3073 | VSpellCheck speller; | ||
| 3074 | ✗ | for (AstClockingItem* itemp = clockingp->itemsp(); itemp; | |
| 3075 | ✗ | itemp = VN_AS(itemp->nextp(), ClockingItem)) { | |
| 3076 | ✗ | if (itemp->varp()->name() == nodep->name()) return itemp->varp(); | |
| 3077 | ✗ | speller.pushCandidate(itemp->varp()->prettyName()); | |
| 3078 | } | ||
| 3079 | ✗ | const string suggest = speller.bestCandidateMsg(nodep->prettyName()); | |
| 3080 | ✗ | nodep->v3error( | |
| 3081 | "Member " << nodep->prettyNameQ() << " not found in clocking block " | ||
| 3082 | << clockingp->prettyNameQ() << "\n" | ||
| 3083 | << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest)); | ||
| 3084 | return nullptr; // Caller handles error | ||
| 3085 | } | ||
| 3086 | ✗ | bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) { | |
| 3087 | // Returns true if ok | ||
| 3088 | ✗ | if (AstMemberDType* const memberp | |
| 3089 | ✗ | = VN_CAST(m_memberMap.findMember(adtypep, nodep->name()), MemberDType)) { | |
| 3090 | ✗ | if (m_attrp) { // Looking for the base of the attribute | |
| 3091 | nodep->dtypep(memberp); | ||
| 3092 | ✗ | UINFO(9, " MEMBERSEL(attr) -> " << nodep << endl); | |
| 3093 | ✗ | UINFO(9, " dt-> " << nodep->dtypep() << endl); | |
| 3094 | ✗ | } else if (adtypep->packed()) { | |
| 3095 | AstSel* const newp = new AstSel{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3096 | ✗ | memberp->lsb(), memberp->width()}; | |
| 3097 | // Must skip over the member to find the union; as the member may disappear later | ||
| 3098 | newp->dtypep(memberp->subDTypep()->skipRefToEnump()); | ||
| 3099 | newp->didWidth(true); // Don't replace dtype with basic type | ||
| 3100 | ✗ | UINFO(9, " MEMBERSEL -> " << newp << endl); | |
| 3101 | ✗ | UINFO(9, " dt-> " << newp->dtypep() << endl); | |
| 3102 | ✗ | nodep->replaceWith(newp); | |
| 3103 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 3104 | // Should be able to treat it as a normal-ish nodesel - maybe. | ||
| 3105 | // The lhsp() will be strange until this stage; create the number here? | ||
| 3106 | } else { | ||
| 3107 | AstStructSel* const newp = new AstStructSel{ | ||
| 3108 | ✗ | nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()}; | |
| 3109 | // Must skip over the member to find the union; as the member may disappear later | ||
| 3110 | newp->dtypep(memberp->subDTypep()->skipRefToEnump()); | ||
| 3111 | newp->didWidth(true); // Don't replace dtype with basic type | ||
| 3112 | ✗ | UINFO(9, " MEMBERSEL -> " << newp << endl); | |
| 3113 | ✗ | UINFO(9, " dt-> " << newp->dtypep() << endl); | |
| 3114 | ✗ | nodep->replaceWith(newp); | |
| 3115 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 3116 | // Should be able to treat it as a normal-ish nodesel - maybe. | ||
| 3117 | // The lhsp() will be strange until this stage; create the number here? | ||
| 3118 | } | ||
| 3119 | ✗ | return true; | |
| 3120 | } | ||
| 3121 | ✗ | nodep->v3error("Member " << nodep->prettyNameQ() << " not found in structure"); | |
| 3122 | ✗ | return false; | |
| 3123 | } | ||
| 3124 | |||
| 3125 | ✗ | void visit(AstCExpr* nodep) override { | |
| 3126 | // Inserted by V3Width only so we know has been resolved | ||
| 3127 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 3128 | } | ||
| 3129 | ✗ | void visit(AstCMethodHard* nodep) override { | |
| 3130 | // Never created before V3Width, so no need to redo it | ||
| 3131 | ✗ | UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized"); | |
| 3132 | } | ||
| 3133 | |||
| 3134 | ✗ | void visit(AstMethodCall* nodep) override { | |
| 3135 | ✗ | UINFO(5, " METHODCALL " << nodep << endl); | |
| 3136 | ✗ | if (nodep->didWidth()) return; | |
| 3137 | ✗ | if (debug() >= 9) nodep->dumpTree("- mts-in: "); | |
| 3138 | // Should check types the method requires, but at present we don't do much | ||
| 3139 | ✗ | userIterate(nodep->fromp(), WidthVP{SELF, BOTH}.p()); | |
| 3140 | // Any AstWith is checked later when know types, in methodWithArgument | ||
| 3141 | ✗ | for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { | |
| 3142 | if (AstArg* const argp = VN_CAST(pinp, Arg)) { | ||
| 3143 | ✗ | if (argp->exprp()) | |
| 3144 | userIterate(argp->exprp(), | ||
| 3145 | ✗ | WidthVP{nodep->fromp()->dtypep()->subDTypep(), BOTH}.p()); | |
| 3146 | } | ||
| 3147 | } | ||
| 3148 | // Find the fromp dtype - should be a class | ||
| 3149 | ✗ | UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression"); | |
| 3150 | AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); | ||
| 3151 | ✗ | AstBasicDType* const basicp = fromDtp ? fromDtp->basicp() : nullptr; | |
| 3152 | ✗ | UINFO(9, " from dt " << fromDtp << endl); | |
| 3153 | ✗ | userIterate(fromDtp, WidthVP{SELF, BOTH}.p()); | |
| 3154 | ✗ | if (nodep->name() == "rand_mode") { | |
| 3155 | ✗ | methodCallRandMode(nodep); | |
| 3156 | ✗ | } else if (nodep->name() == "constraint_mode") { | |
| 3157 | ✗ | methodCallConstraint(nodep, nullptr); | |
| 3158 | } else if (AstEnumDType* const adtypep = VN_CAST(fromDtp, EnumDType)) { | ||
| 3159 | ✗ | methodCallEnum(nodep, adtypep); | |
| 3160 | } else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) { | ||
| 3161 | ✗ | methodCallAssoc(nodep, adtypep); | |
| 3162 | } else if (AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType)) { | ||
| 3163 | ✗ | methodCallWildcard(nodep, adtypep); | |
| 3164 | } else if (AstDynArrayDType* const adtypep = VN_CAST(fromDtp, DynArrayDType)) { | ||
| 3165 | ✗ | methodCallDyn(nodep, adtypep); | |
| 3166 | } else if (AstQueueDType* const adtypep = VN_CAST(fromDtp, QueueDType)) { | ||
| 3167 | ✗ | methodCallQueue(nodep, adtypep); | |
| 3168 | } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) { | ||
| 3169 | ✗ | methodCallClass(nodep, adtypep); | |
| 3170 | } else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) { | ||
| 3171 | ✗ | methodCallIfaceRef(nodep, adtypep); | |
| 3172 | } else if (AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { | ||
| 3173 | ✗ | methodCallUnpack(nodep, adtypep); | |
| 3174 | } else if (AstConstraintRefDType* const adtypep = VN_CAST(fromDtp, ConstraintRefDType)) { | ||
| 3175 | ✗ | methodCallConstraint(nodep, adtypep); | |
| 3176 | ✗ | } else if (basicp && basicp->isEvent()) { | |
| 3177 | ✗ | methodCallEvent(nodep, basicp); | |
| 3178 | ✗ | } else if (basicp && basicp->isString()) { | |
| 3179 | ✗ | methodCallString(nodep, basicp); | |
| 3180 | } else { | ||
| 3181 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: Member call on object '" | |
| 3182 | << nodep->fromp()->prettyTypeName() | ||
| 3183 | << "' which is a '" | ||
| 3184 | << nodep->fromp()->dtypep()->prettyTypeName() << "'"); | ||
| 3185 | ✗ | nodep->dtypep(m_vup->dtypeNullp()); | |
| 3186 | } | ||
| 3187 | } | ||
| 3188 | ✗ | AstWith* methodWithArgument(AstNodeFTaskRef* nodep, bool required, bool arbReturn, | |
| 3189 | AstNodeDType* returnDtp, AstNodeDType* indexDtp, | ||
| 3190 | AstNodeDType* valueDtp) { | ||
| 3191 | ✗ | UASSERT_OBJ(arbReturn || returnDtp, nodep, "Null return type"); | |
| 3192 | ✗ | for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { | |
| 3193 | if (AstWith* const withp = VN_CAST(pinp, With)) { | ||
| 3194 | withp->indexArgRefp()->dtypep(indexDtp); | ||
| 3195 | withp->valueArgRefp()->dtypep(valueDtp); | ||
| 3196 | ✗ | userIterate(withp, WidthVP{returnDtp, BOTH}.p()); | |
| 3197 | withp->unlinkFrBack(); | ||
| 3198 | ✗ | return withp; | |
| 3199 | } | ||
| 3200 | } | ||
| 3201 | ✗ | if (required) { | |
| 3202 | ✗ | nodep->v3error("'with' statement is required for ." << nodep->prettyName() | |
| 3203 | << " method"); | ||
| 3204 | } | ||
| 3205 | return nullptr; | ||
| 3206 | } | ||
| 3207 | ✗ | void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg) { | |
| 3208 | int narg = 0; | ||
| 3209 | ✗ | for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) { | |
| 3210 | if (VN_IS(argp, With)) { | ||
| 3211 | ✗ | argp->v3error("'with' not legal on this method"); | |
| 3212 | // Delete all arguments as nextp() otherwise dangling | ||
| 3213 | ✗ | VL_DO_DANGLING(pushDeletep(argp->unlinkFrBackWithNext()), argp); | |
| 3214 | break; | ||
| 3215 | } | ||
| 3216 | ✗ | ++narg; | |
| 3217 | ✗ | UASSERT_OBJ(VN_IS(argp, Arg), nodep, "Method arg without Arg type"); | |
| 3218 | } | ||
| 3219 | ✗ | const bool ok = (narg >= minArg) && (narg <= maxArg); | |
| 3220 | ✗ | if (!ok) { | |
| 3221 | ✗ | nodep->v3error("The " << narg << " arguments passed to ." << nodep->prettyName() | |
| 3222 | << " method does not match its requiring " << cvtToStr(minArg) | ||
| 3223 | << (minArg == maxArg ? "" : " to " + cvtToStr(maxArg)) | ||
| 3224 | << " arguments"); | ||
| 3225 | // Adjust to required argument counts, very bogus, but avoids core dump | ||
| 3226 | ✗ | for (; narg < minArg; ++narg) { | |
| 3227 | ✗ | nodep->addPinsp( | |
| 3228 | ✗ | new AstArg{nodep->fileline(), "", new AstConst(nodep->fileline(), 0)}); | |
| 3229 | } | ||
| 3230 | ✗ | for (; narg > maxArg; --narg) { | |
| 3231 | AstNode* argp = nodep->pinsp(); | ||
| 3232 | ✗ | while (argp->nextp()) argp = argp->nextp(); | |
| 3233 | ✗ | argp->unlinkFrBack(); | |
| 3234 | ✗ | VL_DO_DANGLING(argp->deleteTree(), argp); | |
| 3235 | } | ||
| 3236 | } | ||
| 3237 | } | ||
| 3238 | |||
| 3239 | ✗ | void methodCallEnum(AstMethodCall* nodep, AstEnumDType* adtypep) { | |
| 3240 | // Method call on enum without following parenthesis, e.g. "ENUM.next" | ||
| 3241 | // Convert this into a method call, and let that visitor figure out what to do next | ||
| 3242 | ✗ | if (nodep->name() == "num" // | |
| 3243 | ✗ | || nodep->name() == "first" // | |
| 3244 | ✗ | || nodep->name() == "last") { | |
| 3245 | // Constant value | ||
| 3246 | AstConst* newp = nullptr; | ||
| 3247 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3248 | ✗ | if (nodep->name() == "num") { | |
| 3249 | int items = 0; | ||
| 3250 | ✗ | for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items; | |
| 3251 | ✗ | newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, items); | |
| 3252 | ✗ | } else if (nodep->name() == "first") { | |
| 3253 | const AstEnumItem* itemp = adtypep->itemsp(); | ||
| 3254 | ✗ | if (!itemp) { | |
| 3255 | newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, | ||
| 3256 | ✗ | 0); // Spec doesn't say what to do | |
| 3257 | } else { | ||
| 3258 | ✗ | newp = VN_AS(itemp->valuep()->cloneTree(false), Const); // A const | |
| 3259 | newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE | ||
| 3260 | } | ||
| 3261 | ✗ | } else if (nodep->name() == "last") { | |
| 3262 | const AstEnumItem* itemp = adtypep->itemsp(); | ||
| 3263 | ✗ | while (itemp && itemp->nextp()) itemp = VN_AS(itemp->nextp(), EnumItem); | |
| 3264 | ✗ | if (!itemp) { | |
| 3265 | newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, | ||
| 3266 | ✗ | 0); // Spec doesn't say what to do | |
| 3267 | } else { | ||
| 3268 | ✗ | newp = VN_AS(itemp->valuep()->cloneTree(false), Const); // A const | |
| 3269 | newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE | ||
| 3270 | } | ||
| 3271 | } | ||
| 3272 | ✗ | UASSERT_OBJ(newp, nodep, "Enum method (perhaps enum item) not const"); | |
| 3273 | newp->fileline(nodep->fileline()); // Use method's filename/line number to be clearer; | ||
| 3274 | // may have warning disables | ||
| 3275 | ✗ | nodep->replaceWith(newp); | |
| 3276 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 3277 | ✗ | } else if (nodep->name() == "name" || nodep->name() == "next" || nodep->name() == "prev") { | |
| 3278 | VAttrType attrType; | ||
| 3279 | ✗ | if (nodep->name() == "name") { | |
| 3280 | ✗ | attrType = VAttrType::ENUM_NAME; | |
| 3281 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3282 | ✗ | } else if (nodep->name() == "next") { | |
| 3283 | ✗ | attrType = VAttrType::ENUM_NEXT; | |
| 3284 | ✗ | methodOkArguments(nodep, 0, 1); | |
| 3285 | ✗ | } else if (nodep->name() == "prev") { | |
| 3286 | ✗ | attrType = VAttrType::ENUM_PREV; | |
| 3287 | ✗ | methodOkArguments(nodep, 0, 1); | |
| 3288 | } else { | ||
| 3289 | ✗ | nodep->v3fatalSrc("Bad case"); | |
| 3290 | } | ||
| 3291 | |||
| 3292 | ✗ | if (nodep->name() != "name" && nodep->pinsp()) { | |
| 3293 | ✗ | if (!VN_IS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const)) { | |
| 3294 | ✗ | nodep->pinsp()->v3fatalSrc( | |
| 3295 | "Unsupported: enum next/prev with non-const argument"); | ||
| 3296 | } else { | ||
| 3297 | const uint32_t stepWidth | ||
| 3298 | ✗ | = VN_AS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const)->toUInt(); | |
| 3299 | ✗ | if (stepWidth == 0) { | |
| 3300 | // Step of 0 "legalizes" like $cast, use .next.prev | ||
| 3301 | AstMethodCall* const newp = new AstMethodCall{ | ||
| 3302 | nodep->fileline(), | ||
| 3303 | new AstMethodCall{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3304 | ✗ | "next", nullptr}, | |
| 3305 | ✗ | "prev", nullptr}; | |
| 3306 | ✗ | nodep->replaceWith(newp); | |
| 3307 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3308 | ✗ | return; | |
| 3309 | ✗ | } else if (stepWidth != 1) { | |
| 3310 | // Unroll of enumVar.next(k) to enumVar.next(1).next(k - 1) | ||
| 3311 | AstMethodCall* const clonep = nodep->cloneTree(false); | ||
| 3312 | ✗ | VN_AS(VN_AS(clonep->pinsp(), Arg)->exprp(), Const)->num().setLong(1); | |
| 3313 | ✗ | AstConst* const constp = new AstConst(nodep->fileline(), stepWidth - 1); | |
| 3314 | ✗ | AstArg* const argp = new AstArg{nodep->fileline(), "", constp}; | |
| 3315 | AstMethodCall* const newp | ||
| 3316 | ✗ | = new AstMethodCall{nodep->fileline(), clonep, nodep->name(), argp}; | |
| 3317 | ✗ | nodep->replaceWith(newp); | |
| 3318 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3319 | ✗ | return; | |
| 3320 | } | ||
| 3321 | } | ||
| 3322 | } | ||
| 3323 | AstNodeExpr* const newp | ||
| 3324 | ✗ | = enumSelect(nodep->fromp()->unlinkFrBack(), adtypep, attrType); | |
| 3325 | ✗ | nodep->replaceWith(newp); | |
| 3326 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3327 | } else { | ||
| 3328 | ✗ | nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ()); | |
| 3329 | } | ||
| 3330 | } | ||
| 3331 | ✗ | void methodCallWildcard(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) { | |
| 3332 | AstCMethodHard* newp = nullptr; | ||
| 3333 | ✗ | if (nodep->name() == "num" // function int num() | |
| 3334 | ✗ | || nodep->name() == "size") { | |
| 3335 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3336 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3337 | ✗ | "size"}; // So don't need num() | |
| 3338 | ✗ | newp->dtypeSetSigned32(); | |
| 3339 | ✗ | } else if (nodep->name() == "first" // function int first(ref index) | |
| 3340 | ✗ | || nodep->name() == "last" // | |
| 3341 | ✗ | || nodep->name() == "next" // | |
| 3342 | ✗ | || nodep->name() == "prev" // | |
| 3343 | ✗ | || nodep->name() == "unique_index" // | |
| 3344 | ✗ | || nodep->name() == "find_index" || nodep->name() == "find_first_index" | |
| 3345 | ✗ | || nodep->name() == "find_last_index") { | |
| 3346 | ✗ | nodep->v3error("Array method " << nodep->prettyNameQ() | |
| 3347 | << " not legal on wildcard associative arrays"); | ||
| 3348 | ✗ | } else if (nodep->name() == "exists") { // function int exists(input index) | |
| 3349 | // IEEE really should have made this a "bit" return | ||
| 3350 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3351 | ✗ | AstNodeExpr* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep); | |
| 3352 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists", | ||
| 3353 | ✗ | index_exprp->unlinkFrBack()}; | |
| 3354 | ✗ | newp->dtypeSetSigned32(); | |
| 3355 | ✗ | } else if (nodep->name() == "delete") { // function void delete([input integer index]) | |
| 3356 | ✗ | methodOkArguments(nodep, 0, 1); | |
| 3357 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3358 | ✗ | if (!nodep->pinsp()) { | |
| 3359 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3360 | ✗ | "clear"}; | |
| 3361 | ✗ | newp->dtypeSetVoid(); | |
| 3362 | } else { | ||
| 3363 | ✗ | AstNodeExpr* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep); | |
| 3364 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3365 | ✗ | "erase", index_exprp->unlinkFrBack()}; | |
| 3366 | ✗ | newp->dtypeSetVoid(); | |
| 3367 | } | ||
| 3368 | ✗ | } else if (nodep->name() == "sort" || nodep->name() == "rsort" | |
| 3369 | ✗ | || nodep->name() == "reverse" || nodep->name() == "shuffle") { | |
| 3370 | ✗ | nodep->v3error("Array method " << nodep->prettyNameQ() | |
| 3371 | << " not legal on associative arrays"); | ||
| 3372 | ✗ | } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" | |
| 3373 | ✗ | || nodep->name() == "sum" || nodep->name() == "product") { | |
| 3374 | // All value return | ||
| 3375 | AstWith* const withp | ||
| 3376 | ✗ | = methodWithArgument(nodep, false, false, adtypep->subDTypep(), | |
| 3377 | adtypep->findStringDType(), adtypep->subDTypep()); | ||
| 3378 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3379 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3380 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3381 | ✗ | "r_" + nodep->name(), withp}; | |
| 3382 | ✗ | newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep()); | |
| 3383 | ✗ | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | |
| 3384 | ✗ | } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique") { | |
| 3385 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3386 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3387 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3388 | ✗ | nodep->name()}; | |
| 3389 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep())); | |
| 3390 | ✗ | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | |
| 3391 | ✗ | } else if (nodep->name() == "find" || nodep->name() == "find_first" | |
| 3392 | ✗ | || nodep->name() == "find_last") { | |
| 3393 | AstWith* const withp | ||
| 3394 | ✗ | = methodWithArgument(nodep, true, false, nodep->findBitDType(), | |
| 3395 | adtypep->findStringDType(), adtypep->subDTypep()); | ||
| 3396 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3397 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3398 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3399 | ✗ | nodep->name(), withp}; | |
| 3400 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep())); | |
| 3401 | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | ||
| 3402 | ✗ | } else if (nodep->name() == "map") { | |
| 3403 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 3404 | "Unsupported: Wildcard array 'map' method (IEEE 1800-2023 7.12.5)"); | ||
| 3405 | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | ||
| 3406 | } else { | ||
| 3407 | ✗ | nodep->v3error("Unknown wildcard associative array method " << nodep->prettyNameQ()); | |
| 3408 | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | ||
| 3409 | } | ||
| 3410 | if (newp) { | ||
| 3411 | newp->protect(false); | ||
| 3412 | newp->didWidth(true); | ||
| 3413 | ✗ | nodep->replaceWith(newp); | |
| 3414 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3415 | } | ||
| 3416 | } | ||
| 3417 | ✗ | void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) { | |
| 3418 | AstCMethodHard* newp = nullptr; | ||
| 3419 | ✗ | if (nodep->name() == "num" // function int num() | |
| 3420 | ✗ | || nodep->name() == "size") { | |
| 3421 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3422 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3423 | ✗ | "size"}; // So don't need num() | |
| 3424 | ✗ | newp->dtypeSetSigned32(); | |
| 3425 | ✗ | } else if (nodep->name() == "first" // function int first(ref index) | |
| 3426 | ✗ | || nodep->name() == "last" // | |
| 3427 | ✗ | || nodep->name() == "next" // | |
| 3428 | ✗ | || nodep->name() == "prev") { | |
| 3429 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3430 | ✗ | AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep); | |
| 3431 | ✗ | methodCallLValueRecurse(nodep, index_exprp, VAccess::READWRITE); | |
| 3432 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3433 | ✗ | nodep->name(), // first/last/next/prev | |
| 3434 | ✗ | index_exprp->unlinkFrBack()}; | |
| 3435 | ✗ | newp->dtypeSetSigned32(); | |
| 3436 | ✗ | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | |
| 3437 | ✗ | } else if (nodep->name() == "exists") { // function int exists(input index) | |
| 3438 | // IEEE really should have made this a "bit" return | ||
| 3439 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3440 | ✗ | AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep); | |
| 3441 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists", | ||
| 3442 | ✗ | index_exprp->unlinkFrBack()}; | |
| 3443 | ✗ | newp->dtypeSetSigned32(); | |
| 3444 | ✗ | } else if (nodep->name() == "delete") { // function void delete([input integer index]) | |
| 3445 | ✗ | methodOkArguments(nodep, 0, 1); | |
| 3446 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3447 | ✗ | if (!nodep->pinsp()) { | |
| 3448 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3449 | ✗ | "clear"}; | |
| 3450 | ✗ | newp->dtypeSetVoid(); | |
| 3451 | } else { | ||
| 3452 | ✗ | AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep); | |
| 3453 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3454 | ✗ | "erase", index_exprp->unlinkFrBack()}; | |
| 3455 | ✗ | newp->dtypeSetVoid(); | |
| 3456 | } | ||
| 3457 | ✗ | } else if (nodep->name() == "sort" || nodep->name() == "rsort" | |
| 3458 | ✗ | || nodep->name() == "reverse" || nodep->name() == "shuffle") { | |
| 3459 | ✗ | nodep->v3error("Array method " << nodep->prettyNameQ() | |
| 3460 | << " not legal on associative arrays"); | ||
| 3461 | ✗ | } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" | |
| 3462 | ✗ | || nodep->name() == "sum" || nodep->name() == "product") { | |
| 3463 | // All value return | ||
| 3464 | ✗ | AstWith* const withp = methodWithArgument(nodep, false, false, adtypep->subDTypep(), | |
| 3465 | adtypep->keyDTypep(), adtypep->subDTypep()); | ||
| 3466 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3467 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3468 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3469 | ✗ | "r_" + nodep->name(), withp}; | |
| 3470 | ✗ | newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep()); | |
| 3471 | ✗ | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | |
| 3472 | ✗ | } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" | |
| 3473 | ✗ | || nodep->name() == "unique_index") { | |
| 3474 | ✗ | AstWith* const withp = methodWithArgument( | |
| 3475 | nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep()); | ||
| 3476 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3477 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3478 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3479 | ✗ | nodep->name(), withp}; | |
| 3480 | ✗ | if (nodep->name() == "unique_index") { | |
| 3481 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep())); | |
| 3482 | } else { | ||
| 3483 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep())); | |
| 3484 | } | ||
| 3485 | ✗ | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | |
| 3486 | ✗ | } else if (nodep->name() == "find" || nodep->name() == "find_first" | |
| 3487 | ✗ | || nodep->name() == "find_last") { | |
| 3488 | ✗ | AstWith* const withp = methodWithArgument(nodep, true, false, nodep->findBitDType(), | |
| 3489 | adtypep->keyDTypep(), adtypep->subDTypep()); | ||
| 3490 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3491 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3492 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3493 | ✗ | nodep->name(), withp}; | |
| 3494 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep())); | |
| 3495 | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | ||
| 3496 | ✗ | } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index" | |
| 3497 | ✗ | || nodep->name() == "find_last_index") { | |
| 3498 | ✗ | AstWith* const withp = methodWithArgument(nodep, true, false, nodep->findBitDType(), | |
| 3499 | adtypep->keyDTypep(), adtypep->subDTypep()); | ||
| 3500 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3501 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3502 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3503 | ✗ | nodep->name(), withp}; | |
| 3504 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep())); | |
| 3505 | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | ||
| 3506 | ✗ | } else if (nodep->name() == "map") { | |
| 3507 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 3508 | "Unsupported: Associative array 'map' method (IEEE 1800-2023 7.12.5)"); | ||
| 3509 | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | ||
| 3510 | } else { | ||
| 3511 | ✗ | nodep->v3error("Unknown built-in associative array method " << nodep->prettyNameQ()); | |
| 3512 | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | ||
| 3513 | } | ||
| 3514 | if (newp) { | ||
| 3515 | newp->protect(false); | ||
| 3516 | newp->didWidth(true); | ||
| 3517 | ✗ | nodep->replaceWith(newp); | |
| 3518 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3519 | } | ||
| 3520 | } | ||
| 3521 | ✗ | AstNodeExpr* methodCallAssocIndexExpr(AstMethodCall* nodep, AstAssocArrayDType* adtypep) { | |
| 3522 | AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp(); | ||
| 3523 | ✗ | iterateCheck(nodep, "index", index_exprp, CONTEXT_DET, FINAL, adtypep->keyDTypep(), | |
| 3524 | EXTEND_EXP); | ||
| 3525 | VL_DANGLING(index_exprp); // May have been edited | ||
| 3526 | ✗ | return VN_AS(nodep->pinsp(), Arg)->exprp(); | |
| 3527 | } | ||
| 3528 | ✗ | AstNodeExpr* methodCallWildcardIndexExpr(AstMethodCall* nodep, | |
| 3529 | AstWildcardArrayDType* adtypep) { | ||
| 3530 | AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp(); | ||
| 3531 | ✗ | iterateCheck(nodep, "index", index_exprp, CONTEXT_DET, FINAL, adtypep->findStringDType(), | |
| 3532 | EXTEND_EXP); | ||
| 3533 | VL_DANGLING(index_exprp); // May have been edited | ||
| 3534 | ✗ | return VN_AS(nodep->pinsp(), Arg)->exprp(); | |
| 3535 | } | ||
| 3536 | ✗ | void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) { | |
| 3537 | if (AstCMethodHard* const ichildp = VN_CAST(childp, CMethodHard)) { | ||
| 3538 | const std::string name = ichildp->name(); | ||
| 3539 | ✗ | if (name == "at" || name == "atWrite" || name == "atBack" || name == "atWriteAppend" | |
| 3540 | ✗ | || name == "atWriteAppendBack") { | |
| 3541 | const AstNodeDType* const fromDtypep = ichildp->fromp()->dtypep()->skipRefp(); | ||
| 3542 | if (VN_IS(fromDtypep, QueueDType) || VN_IS(fromDtypep, DynArrayDType)) { | ||
| 3543 | // Change access methods to writable ones | ||
| 3544 | ✗ | if (name == "at") { | |
| 3545 | ✗ | ichildp->name("atWrite"); | |
| 3546 | ✗ | } else if (name == "atBack") { | |
| 3547 | ✗ | ichildp->name("atWriteAppendBack"); | |
| 3548 | } | ||
| 3549 | } | ||
| 3550 | ✗ | methodCallLValueRecurse(nodep, ichildp->fromp(), access); | |
| 3551 | return; | ||
| 3552 | } | ||
| 3553 | } | ||
| 3554 | if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) { | ||
| 3555 | varrefp->access(access); | ||
| 3556 | } else if (const AstMemberSel* const ichildp = VN_CAST(childp, MemberSel)) { | ||
| 3557 | methodCallLValueRecurse(nodep, ichildp->fromp(), access); | ||
| 3558 | } else if (const AstStructSel* const ichildp = VN_CAST(childp, StructSel)) { | ||
| 3559 | methodCallLValueRecurse(nodep, ichildp->fromp(), access); | ||
| 3560 | } else if (const AstNodeSel* const ichildp = VN_CAST(childp, NodeSel)) { | ||
| 3561 | methodCallLValueRecurse(nodep, ichildp->fromp(), access); | ||
| 3562 | } else { | ||
| 3563 | ✗ | UINFO(1, " Related node: " << childp << endl); | |
| 3564 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-variable on LHS of built-in method '" | |
| 3565 | << nodep->prettyName() << "'"); | ||
| 3566 | } | ||
| 3567 | } | ||
| 3568 | ✗ | AstCMethodHard* methodCallArray(AstMethodCall* nodep, AstNodeDType* adtypep) { | |
| 3569 | AstCMethodHard* newp = nullptr; | ||
| 3570 | |||
| 3571 | ✗ | if (nodep->name() == "reverse" || nodep->name() == "shuffle" || nodep->name() == "sort" | |
| 3572 | ✗ | || nodep->name() == "rsort") { | |
| 3573 | AstWith* withp = nullptr; | ||
| 3574 | ✗ | if (nodep->name() == "sort" || nodep->name() == "rsort") { | |
| 3575 | ✗ | withp = methodWithArgument(nodep, false, true, nullptr, nodep->findUInt32DType(), | |
| 3576 | ✗ | adtypep->subDTypep()); | |
| 3577 | } | ||
| 3578 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3579 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3580 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3581 | ✗ | nodep->name(), withp}; | |
| 3582 | ✗ | newp->dtypeSetVoid(); | |
| 3583 | ✗ | } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" | |
| 3584 | ✗ | || nodep->name() == "unique_index") { | |
| 3585 | ✗ | AstWith* const withp = methodWithArgument( | |
| 3586 | ✗ | nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep()); | |
| 3587 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3588 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3589 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3590 | ✗ | nodep->name(), withp}; | |
| 3591 | ✗ | if (nodep->name() == "unique_index") { | |
| 3592 | ✗ | newp->dtypep(newp->findQueueIndexDType()); | |
| 3593 | } else { | ||
| 3594 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep())); | |
| 3595 | } | ||
| 3596 | ✗ | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | |
| 3597 | ✗ | } else if (nodep->name() == "find" || nodep->name() == "find_first" | |
| 3598 | ✗ | || nodep->name() == "find_last" || nodep->name() == "find_index") { | |
| 3599 | AstWith* const withp | ||
| 3600 | ✗ | = methodWithArgument(nodep, true, false, nodep->findBitDType(), | |
| 3601 | ✗ | nodep->findUInt32DType(), adtypep->subDTypep()); | |
| 3602 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3603 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3604 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3605 | ✗ | nodep->name(), withp}; | |
| 3606 | ✗ | newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep())); | |
| 3607 | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | ||
| 3608 | ✗ | } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index" | |
| 3609 | ✗ | || nodep->name() == "find_last_index") { | |
| 3610 | AstWith* const withp | ||
| 3611 | ✗ | = methodWithArgument(nodep, true, false, nodep->findBitDType(), | |
| 3612 | ✗ | nodep->findUInt32DType(), adtypep->subDTypep()); | |
| 3613 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3614 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3615 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3616 | ✗ | nodep->name(), withp}; | |
| 3617 | ✗ | newp->dtypep(newp->findQueueIndexDType()); | |
| 3618 | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | ||
| 3619 | ✗ | } else if (nodep->name() == "map") { | |
| 3620 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 3621 | "Unsupported: Array 'map' method (IEEE 1800-2023 7.12.5)"); | ||
| 3622 | ✗ | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | |
| 3623 | } | ||
| 3624 | ✗ | return newp; | |
| 3625 | } | ||
| 3626 | ✗ | void methodCallDyn(AstMethodCall* nodep, AstDynArrayDType* adtypep) { | |
| 3627 | AstCMethodHard* newp = nullptr; | ||
| 3628 | ✗ | if (nodep->name() == "at") { // Created internally for [] | |
| 3629 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3630 | ✗ | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at"}; | |
| 3631 | newp->dtypeFrom(adtypep->subDTypep()); | ||
| 3632 | ✗ | } else if (nodep->name() == "atWrite") { // Created internally for [] | |
| 3633 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3634 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3635 | newp | ||
| 3636 | ✗ | = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "atWrite"}; | |
| 3637 | newp->dtypeFrom(adtypep->subDTypep()); | ||
| 3638 | ✗ | } else if (nodep->name() == "size") { | |
| 3639 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3640 | ✗ | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size"}; | |
| 3641 | ✗ | newp->dtypeSetSigned32(); | |
| 3642 | ✗ | } else if (nodep->name() == "delete") { // function void delete() | |
| 3643 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3644 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3645 | ✗ | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "clear"}; | |
| 3646 | ✗ | newp->dtypeSetVoid(); | |
| 3647 | ✗ | } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" | |
| 3648 | ✗ | || nodep->name() == "sum" || nodep->name() == "product") { | |
| 3649 | // All value return | ||
| 3650 | AstWith* const withp | ||
| 3651 | ✗ | = methodWithArgument(nodep, false, false, adtypep->subDTypep(), | |
| 3652 | nodep->findUInt32DType(), adtypep->subDTypep()); | ||
| 3653 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3654 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3655 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3656 | ✗ | "r_" + nodep->name(), withp}; | |
| 3657 | ✗ | newp->dtypeFrom(adtypep->subDTypep()); | |
| 3658 | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | ||
| 3659 | ✗ | } else if ((newp = methodCallArray(nodep, adtypep))) { | |
| 3660 | } else { | ||
| 3661 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in dynamic array method " | |
| 3662 | << nodep->prettyNameQ()); | ||
| 3663 | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | ||
| 3664 | } | ||
| 3665 | ✗ | if (newp) { | |
| 3666 | newp->protect(false); | ||
| 3667 | newp->didWidth(true); | ||
| 3668 | ✗ | nodep->replaceWith(newp); | |
| 3669 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3670 | } | ||
| 3671 | } | ||
| 3672 | ✗ | void methodCallQueue(AstMethodCall* nodep, AstQueueDType* adtypep) { | |
| 3673 | AstCMethodHard* newp = nullptr; | ||
| 3674 | ✗ | if (nodep->name() == "at" || nodep->name() == "atBack") { // Created internally for [] | |
| 3675 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3676 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3677 | ✗ | nodep->name()}; | |
| 3678 | newp->dtypeFrom(adtypep->subDTypep()); | ||
| 3679 | ✗ | } else if (nodep->name() == "atWriteAppend" | |
| 3680 | ✗ | || nodep->name() == "atWriteAppendBack") { // Created internally for [] | |
| 3681 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3682 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3683 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3684 | ✗ | nodep->name()}; | |
| 3685 | newp->dtypeFrom(adtypep->subDTypep()); | ||
| 3686 | ✗ | } else if (nodep->name() == "num" // function int num() | |
| 3687 | ✗ | || nodep->name() == "size") { | |
| 3688 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3689 | ✗ | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size"}; | |
| 3690 | ✗ | newp->dtypeSetSigned32(); | |
| 3691 | ✗ | } else if (nodep->name() == "delete") { // function void delete([input integer index]) | |
| 3692 | ✗ | methodOkArguments(nodep, 0, 1); | |
| 3693 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3694 | ✗ | if (!nodep->pinsp()) { | |
| 3695 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3696 | ✗ | "clear"}; | |
| 3697 | ✗ | newp->dtypeSetVoid(); | |
| 3698 | } else { | ||
| 3699 | ✗ | AstNodeExpr* const index_exprp = methodCallQueueIndexExpr(nodep); | |
| 3700 | ✗ | if (index_exprp->isZero()) { // delete(0) is a pop_front | |
| 3701 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3702 | ✗ | "pop_front"}; | |
| 3703 | ✗ | newp->dtypeFrom(adtypep->subDTypep()); | |
| 3704 | newp->dtypeSetVoid(); | ||
| 3705 | } else { | ||
| 3706 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3707 | ✗ | "erase", index_exprp->unlinkFrBack()}; | |
| 3708 | ✗ | newp->dtypeSetVoid(); | |
| 3709 | } | ||
| 3710 | } | ||
| 3711 | ✗ | } else if (nodep->name() == "insert") { | |
| 3712 | ✗ | methodOkArguments(nodep, 2, 2); | |
| 3713 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3714 | ✗ | AstNodeExpr* const index_exprp = methodCallQueueIndexExpr(nodep); | |
| 3715 | ✗ | AstArg* const argp = VN_AS(nodep->pinsp()->nextp(), Arg); | |
| 3716 | ✗ | iterateCheckTyped(nodep, "insert value", argp->exprp(), adtypep->subDTypep(), BOTH); | |
| 3717 | ✗ | if (index_exprp->isZero()) { // insert(0, ...) is a push_front | |
| 3718 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3719 | ✗ | "push_front", argp->exprp()->unlinkFrBack()}; | |
| 3720 | ✗ | newp->dtypeSetVoid(); | |
| 3721 | } else { | ||
| 3722 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3723 | ✗ | nodep->name(), index_exprp->unlinkFrBack()}; | |
| 3724 | newp->addPinsp(argp->exprp()->unlinkFrBack()); | ||
| 3725 | newp->dtypeSetVoid(); | ||
| 3726 | } | ||
| 3727 | ✗ | } else if (nodep->name() == "pop_front" || nodep->name() == "pop_back") { | |
| 3728 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3729 | // Returns element, so method both consumes (reads) and modifies the queue | ||
| 3730 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READWRITE); | |
| 3731 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3732 | ✗ | nodep->name()}; | |
| 3733 | ✗ | newp->dtypeFrom(adtypep->subDTypep()); | |
| 3734 | ✗ | if (nodep->isStandaloneBodyStmt()) newp->dtypeSetVoid(); | |
| 3735 | ✗ | } else if (nodep->name() == "push_back" || nodep->name() == "push_front") { | |
| 3736 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3737 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3738 | ✗ | AstArg* const argp = VN_AS(nodep->pinsp(), Arg); | |
| 3739 | ✗ | iterateCheckTyped(nodep, "push value", argp->exprp(), adtypep->subDTypep(), BOTH); | |
| 3740 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3741 | ✗ | nodep->name(), argp->exprp()->unlinkFrBack()}; | |
| 3742 | ✗ | newp->dtypeSetVoid(); | |
| 3743 | ✗ | } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" | |
| 3744 | ✗ | || nodep->name() == "sum" || nodep->name() == "product") { | |
| 3745 | AstWith* const withp | ||
| 3746 | ✗ | = methodWithArgument(nodep, false, false, adtypep->subDTypep(), | |
| 3747 | nodep->findUInt32DType(), adtypep->subDTypep()); | ||
| 3748 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 3749 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); | |
| 3750 | newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), | ||
| 3751 | ✗ | "r_" + nodep->name(), withp}; | |
| 3752 | ✗ | newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep()); | |
| 3753 | if (!nodep->firstAbovep()) newp->dtypeSetVoid(); | ||
| 3754 | ✗ | } else if ((newp = methodCallArray(nodep, adtypep))) { | |
| 3755 | } else { | ||
| 3756 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 3757 | "Unsupported/unknown built-in queue method " << nodep->prettyNameQ()); | ||
| 3758 | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | ||
| 3759 | } | ||
| 3760 | ✗ | if (newp) { | |
| 3761 | newp->protect(false); | ||
| 3762 | newp->didWidth(true); | ||
| 3763 | ✗ | nodep->replaceWith(newp); | |
| 3764 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3765 | } | ||
| 3766 | } | ||
| 3767 | ✗ | AstNodeExpr* methodCallQueueIndexExpr(AstMethodCall* nodep) { | |
| 3768 | ✗ | AstNode* const index_exprp = VN_AS(nodep->pinsp(), Arg)->exprp(); | |
| 3769 | ✗ | iterateCheckSigned32(nodep, "index", index_exprp, BOTH); | |
| 3770 | VL_DANGLING(index_exprp); // May have been edited | ||
| 3771 | ✗ | return VN_AS(nodep->pinsp(), Arg)->exprp(); | |
| 3772 | } | ||
| 3773 | ✗ | void methodCallWarnTiming(AstNodeFTaskRef* const nodep, const std::string& className) { | |
| 3774 | ✗ | if (v3Global.opt.timing().isSetFalse()) { | |
| 3775 | ✗ | nodep->v3warn(E_NOTIMING, | |
| 3776 | className << "::" << nodep->name() << "() requires --timing"); | ||
| 3777 | ✗ | } else if (!v3Global.opt.timing().isSetTrue()) { | |
| 3778 | ✗ | nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how " | |
| 3779 | << className << "::" << nodep->name() | ||
| 3780 | << "() should be handled"); | ||
| 3781 | } | ||
| 3782 | } | ||
| 3783 | ✗ | void methodCallIfaceRef(AstMethodCall* nodep, AstIfaceRefDType* adtypep) { | |
| 3784 | AstIface* const ifacep = adtypep->ifacep(); | ||
| 3785 | ✗ | UINFO(5, __FUNCTION__ << ":" << nodep << endl); | |
| 3786 | ✗ | if (AstNodeFTask* const ftaskp | |
| 3787 | ✗ | = VN_CAST(m_memberMap.findMember(ifacep, nodep->name()), NodeFTask)) { | |
| 3788 | ✗ | UINFO(5, __FUNCTION__ << "AstNodeFTask" << nodep << endl); | |
| 3789 | userIterate(ftaskp, nullptr); | ||
| 3790 | ✗ | if (ftaskp->isStatic()) { | |
| 3791 | AstNodeExpr* argsp = nullptr; | ||
| 3792 | ✗ | if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext(); | |
| 3793 | AstNodeFTaskRef* newp = nullptr; | ||
| 3794 | if (VN_IS(ftaskp, Task)) { | ||
| 3795 | ✗ | newp = new AstTaskRef{nodep->fileline(), VN_AS(ftaskp, Task), argsp}; | |
| 3796 | } else { | ||
| 3797 | ✗ | newp = new AstFuncRef{nodep->fileline(), VN_AS(ftaskp, Func), argsp}; | |
| 3798 | } | ||
| 3799 | newp->classOrPackagep(ifacep); | ||
| 3800 | ✗ | nodep->replaceWith(newp); | |
| 3801 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3802 | } else { | ||
| 3803 | nodep->taskp(ftaskp); | ||
| 3804 | ✗ | nodep->dtypeFrom(ftaskp); | |
| 3805 | nodep->classOrPackagep(ifacep); | ||
| 3806 | if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid(); | ||
| 3807 | ✗ | processFTaskRefArgs(nodep); | |
| 3808 | } | ||
| 3809 | ✗ | return; | |
| 3810 | } | ||
| 3811 | ✗ | nodep->v3error("Member reference from interface to " | |
| 3812 | << nodep->prettyNameQ() << " is not referencing a valid task or function "); | ||
| 3813 | } | ||
| 3814 | ✗ | void handleRandomizeArgs(AstNodeFTaskRef* const nodep, AstClass* const classp) { | |
| 3815 | bool hasNonNullArgs = false; | ||
| 3816 | AstConst* nullp = nullptr; | ||
| 3817 | ✗ | for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) { | |
| 3818 | nextp = pinp->nextp(); | ||
| 3819 | AstArg* const argp = VN_CAST(pinp, Arg); | ||
| 3820 | ✗ | if (!argp) continue; | |
| 3821 | ✗ | AstVar* randVarp = nullptr; | |
| 3822 | AstNodeExpr* exprp = argp->exprp(); | ||
| 3823 | if (AstConst* const constp = VN_CAST(exprp, Const)) { | ||
| 3824 | ✗ | if (constp->num().isNull()) { | |
| 3825 | nullp = constp; | ||
| 3826 | ✗ | continue; | |
| 3827 | } | ||
| 3828 | } | ||
| 3829 | hasNonNullArgs = true; | ||
| 3830 | AstVar* fromVarp = nullptr; // If it's a method call, the leftmost element | ||
| 3831 | // of the dot hierarchy | ||
| 3832 | if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) { | ||
| 3833 | AstNodeExpr* fromp = methodCallp->fromp(); | ||
| 3834 | while (AstMemberSel* const memberSelp = VN_CAST(fromp, MemberSel)) { | ||
| 3835 | fromp = memberSelp->fromp(); | ||
| 3836 | } | ||
| 3837 | ✗ | AstVarRef* const varrefp = VN_AS(fromp, VarRef); | |
| 3838 | fromVarp = varrefp->varp(); | ||
| 3839 | } | ||
| 3840 | ✗ | if (!VN_IS(exprp, VarRef) && !VN_IS(exprp, MemberSel)) { | |
| 3841 | ✗ | argp->v3error("'randomize()' argument must be a variable contained in " | |
| 3842 | << (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ())); | ||
| 3843 | ✗ | VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp); | |
| 3844 | ✗ | continue; | |
| 3845 | } | ||
| 3846 | ✗ | while (exprp) { | |
| 3847 | if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) { | ||
| 3848 | ✗ | randVarp = memberSelp->varp(); | |
| 3849 | exprp = memberSelp->fromp(); | ||
| 3850 | } else { | ||
| 3851 | if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) { | ||
| 3852 | ✗ | randVarp = varrefp->varp(); | |
| 3853 | } else { | ||
| 3854 | ✗ | argp->v3warn( | |
| 3855 | E_UNSUPPORTED, | ||
| 3856 | "Unsupported: Non-variable expression as 'randomize()' argument"); | ||
| 3857 | ✗ | VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp); | |
| 3858 | } | ||
| 3859 | exprp = nullptr; | ||
| 3860 | } | ||
| 3861 | // All variables in the dot hierarchy must be randomizable | ||
| 3862 | ✗ | if (randVarp && !randVarp->isRand()) randVarp->rand(VRandAttr::RAND_INLINE); | |
| 3863 | } | ||
| 3864 | ✗ | if (!argp) continue; // Errored out, bail | |
| 3865 | // randVarp is now the leftmost element from the dot hierarchy in argp->exprp() | ||
| 3866 | ✗ | if (randVarp == fromVarp) { | |
| 3867 | // The passed in variable is MemberSel'ected from the MethodCall target | ||
| 3868 | ✗ | } else if (classp->existsMember([&](const AstClass*, const AstVar* memberVarp) { | |
| 3869 | ✗ | return memberVarp == randVarp; | |
| 3870 | })) { | ||
| 3871 | // The passed in variable is contained in the method call target | ||
| 3872 | } else { | ||
| 3873 | // Passed in a constant or complex expression, or the above conditions are not | ||
| 3874 | // met | ||
| 3875 | ✗ | argp->v3error("'randomize()' argument must be a variable contained in " | |
| 3876 | << (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ())); | ||
| 3877 | ✗ | VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp); | |
| 3878 | } | ||
| 3879 | } | ||
| 3880 | ✗ | if (nullp) { | |
| 3881 | ✗ | if (hasNonNullArgs) { | |
| 3882 | ✗ | nullp->v3error("Cannot pass more arguments to 'randomize(null)'"); | |
| 3883 | } else { | ||
| 3884 | ✗ | nullp->v3warn(E_UNSUPPORTED, "Unsupported: 'randomize(null)'"); | |
| 3885 | } | ||
| 3886 | } | ||
| 3887 | } | ||
| 3888 | ✗ | void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) { | |
| 3889 | // No need to width-resolve the class, as it was done when we did the child | ||
| 3890 | AstClass* const first_classp = adtypep->classp(); | ||
| 3891 | AstWith* withp = nullptr; | ||
| 3892 | ✗ | if (nodep->name() == "randomize") { | |
| 3893 | ✗ | VL_RESTORER(m_randomizeFromp); | |
| 3894 | ✗ | m_randomizeFromp = nodep->fromp(); | |
| 3895 | ✗ | withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(), | |
| 3896 | adtypep->findBitDType(), adtypep); | ||
| 3897 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3898 | ✗ | V3Randomize::newRandomizeFunc(m_memberMap, first_classp); | |
| 3899 | ✗ | handleRandomizeArgs(nodep, first_classp); | |
| 3900 | ✗ | } else if (nodep->name() == "srandom") { | |
| 3901 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 3902 | ✗ | methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); | |
| 3903 | ✗ | V3Randomize::newSRandomFunc(m_memberMap, first_classp); | |
| 3904 | } | ||
| 3905 | ✗ | UASSERT_OBJ(first_classp, nodep, "Unlinked"); | |
| 3906 | ✗ | for (AstClass* classp = first_classp; classp;) { | |
| 3907 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 3908 | ✗ | if (classp->name() == "semaphore" || classp->name() == "process" | |
| 3909 | ✗ | || VString::startsWith(classp->name(), "mailbox")) { | |
| 3910 | // Find the package the class is in | ||
| 3911 | AstPackage* const packagep = getItemPackage(classp); | ||
| 3912 | // Check if it's std | ||
| 3913 | ✗ | if (packagep && packagep->name() == "std") { | |
| 3914 | ✗ | if (classp->name() == "process") { | |
| 3915 | ✗ | methodCallWarnTiming(nodep, "process"); | |
| 3916 | ✗ | } else if (classp->name() == "semaphore" && nodep->name() == "get") { | |
| 3917 | ✗ | methodCallWarnTiming(nodep, "semaphore"); | |
| 3918 | ✗ | } else if (nodep->name() == "put" || nodep->name() == "get" | |
| 3919 | ✗ | || nodep->name() == "peek") { | |
| 3920 | ✗ | methodCallWarnTiming(nodep, "mailbox"); | |
| 3921 | } | ||
| 3922 | } | ||
| 3923 | } | ||
| 3924 | } | ||
| 3925 | ✗ | if (AstNodeFTask* const ftaskp | |
| 3926 | ✗ | = VN_CAST(m_memberMap.findMember(classp, nodep->name()), NodeFTask)) { | |
| 3927 | userIterate(ftaskp, nullptr); | ||
| 3928 | ✗ | if (ftaskp->isStatic()) { | |
| 3929 | AstNodeExpr* argsp = nullptr; | ||
| 3930 | ✗ | if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext(); | |
| 3931 | AstNodeFTaskRef* newp = nullptr; | ||
| 3932 | // We use m_vup to determine task or function, so that later error checks | ||
| 3933 | // for funcref->task and taskref->func will pick up properly | ||
| 3934 | ✗ | if (!m_vup) { // Called as task | |
| 3935 | ✗ | newp = new AstTaskRef{nodep->fileline(), nodep->name(), argsp}; | |
| 3936 | } else { | ||
| 3937 | ✗ | newp = new AstFuncRef{nodep->fileline(), nodep->name(), argsp}; | |
| 3938 | } | ||
| 3939 | newp->taskp(ftaskp); // Not passed above as might be func vs task mismatch | ||
| 3940 | newp->classOrPackagep(classp); | ||
| 3941 | ✗ | nodep->replaceWith(newp); | |
| 3942 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 3943 | } else { | ||
| 3944 | nodep->taskp(ftaskp); | ||
| 3945 | ✗ | nodep->dtypeFrom(ftaskp); | |
| 3946 | nodep->classOrPackagep(classp); | ||
| 3947 | if (VN_IS(ftaskp, Task)) { | ||
| 3948 | ✗ | if (!m_vup) { | |
| 3949 | nodep->dtypeSetVoid(); | ||
| 3950 | } else { | ||
| 3951 | ✗ | if (m_vup->prelim()) { | |
| 3952 | ✗ | nodep->v3error( | |
| 3953 | "Cannot call a task/void-function as a member function: " | ||
| 3954 | << nodep->prettyNameQ()); | ||
| 3955 | } | ||
| 3956 | nodep->dtypeSetVoid(); | ||
| 3957 | } | ||
| 3958 | } | ||
| 3959 | ✗ | if (withp) nodep->addPinsp(withp); | |
| 3960 | ✗ | processFTaskRefArgs(nodep); | |
| 3961 | } | ||
| 3962 | ✗ | return; | |
| 3963 | ✗ | } else if (nodep->name() == "get_randstate" || nodep->name() == "set_randstate") { | |
| 3964 | // See implementations under AstNodeFTaskRef | ||
| 3965 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'get_randstate'/'set_randstate' called " | |
| 3966 | "on object. Suggest call from inside class."); | ||
| 3967 | ✗ | nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}}); | |
| 3968 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 3969 | ✗ | return; | |
| 3970 | ✗ | } else if (nodep->name() == "constraint_mode") { | |
| 3971 | v3Global.useRandomizeMethods(true); | ||
| 3972 | ✗ | nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT)); | |
| 3973 | } | ||
| 3974 | ✗ | classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; | |
| 3975 | } | ||
| 3976 | { | ||
| 3977 | VSpellCheck speller; | ||
| 3978 | ✗ | for (AstClass* classp = first_classp; classp;) { | |
| 3979 | ✗ | for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) { | |
| 3980 | ✗ | if (VN_IS(itemp, NodeFTask)) speller.pushCandidate(itemp->prettyName()); | |
| 3981 | } | ||
| 3982 | ✗ | classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; | |
| 3983 | } | ||
| 3984 | ✗ | const string suggest = speller.bestCandidateMsg(nodep->prettyName()); | |
| 3985 | ✗ | nodep->v3error("Class method " | |
| 3986 | << nodep->prettyNameQ() << " not found in class " | ||
| 3987 | << first_classp->prettyNameQ() << "\n" | ||
| 3988 | << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest)); | ||
| 3989 | } | ||
| 3990 | ✗ | nodep->dtypeSetSigned32(); // Guess on error | |
| 3991 | } | ||
| 3992 | ✗ | void methodCallConstraint(AstMethodCall* nodep, AstConstraintRefDType*) { | |
| 3993 | ✗ | if (nodep->name() == "constraint_mode") { | |
| 3994 | // IEEE 1800-2023 18.9 | ||
| 3995 | ✗ | methodOkArguments(nodep, 0, 1); | |
| 3996 | ✗ | if (nodep->pinsp()) { | |
| 3997 | ✗ | nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT)); | |
| 3998 | } else { | ||
| 3999 | ✗ | nodep->dtypeSetVoid(); | |
| 4000 | } | ||
| 4001 | v3Global.useRandomizeMethods(true); | ||
| 4002 | } else { | ||
| 4003 | ✗ | nodep->v3error("No such constraint method " << nodep->prettyNameQ()); | |
| 4004 | ✗ | nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); | |
| 4005 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 4006 | } | ||
| 4007 | } | ||
| 4008 | ✗ | void methodCallRandMode(AstMethodCall* nodep) { | |
| 4009 | ✗ | methodOkArguments(nodep, 0, 1); | |
| 4010 | // IEEE 1800-2023 18.8 | ||
| 4011 | ✗ | if (nodep->pinsp()) { | |
| 4012 | ✗ | nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT)); | |
| 4013 | } else { | ||
| 4014 | ✗ | nodep->dtypeSetVoid(); | |
| 4015 | } | ||
| 4016 | v3Global.useRandomizeMethods(true); | ||
| 4017 | } | ||
| 4018 | ✗ | void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) { | |
| 4019 | enum : uint8_t { | ||
| 4020 | UNKNOWN = 0, | ||
| 4021 | ARRAY_OR, | ||
| 4022 | ARRAY_AND, | ||
| 4023 | ARRAY_XOR, | ||
| 4024 | ARRAY_SUM, | ||
| 4025 | ARRAY_PRODUCT | ||
| 4026 | } methodId; | ||
| 4027 | |||
| 4028 | methodId = UNKNOWN; | ||
| 4029 | ✗ | if (nodep->name() == "or") { | |
| 4030 | methodId = ARRAY_OR; | ||
| 4031 | ✗ | } else if (nodep->name() == "and") { | |
| 4032 | methodId = ARRAY_AND; | ||
| 4033 | ✗ | } else if (nodep->name() == "xor") { | |
| 4034 | methodId = ARRAY_XOR; | ||
| 4035 | ✗ | } else if (nodep->name() == "sum") { | |
| 4036 | methodId = ARRAY_SUM; | ||
| 4037 | ✗ | } else if (nodep->name() == "product") { | |
| 4038 | methodId = ARRAY_PRODUCT; | ||
| 4039 | } | ||
| 4040 | |||
| 4041 | if (methodId) { | ||
| 4042 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 4043 | FileLine* const fl = nodep->fileline(); | ||
| 4044 | AstNodeExpr* newp = nullptr; | ||
| 4045 | ✗ | for (int i = 0; i < adtypep->elementsConst(); ++i) { | |
| 4046 | AstNodeExpr* const arrayRef = nodep->fromp()->cloneTreePure(false); | ||
| 4047 | ✗ | AstNodeExpr* const selector = new AstArraySel{fl, arrayRef, i}; | |
| 4048 | ✗ | if (!newp) { | |
| 4049 | newp = selector; | ||
| 4050 | } else { | ||
| 4051 | ✗ | switch (methodId) { | |
| 4052 | ✗ | case ARRAY_OR: newp = new AstOr{fl, newp, selector}; break; | |
| 4053 | ✗ | case ARRAY_AND: newp = new AstAnd{fl, newp, selector}; break; | |
| 4054 | ✗ | case ARRAY_XOR: newp = new AstXor{fl, newp, selector}; break; | |
| 4055 | ✗ | case ARRAY_SUM: newp = new AstAdd{fl, newp, selector}; break; | |
| 4056 | ✗ | case ARRAY_PRODUCT: newp = new AstMul{fl, newp, selector}; break; | |
| 4057 | default: nodep->v3fatalSrc("bad case"); | ||
| 4058 | } | ||
| 4059 | } | ||
| 4060 | } | ||
| 4061 | ✗ | nodep->replaceWith(newp); | |
| 4062 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4063 | ✗ | } else if (AstCMethodHard* newp = methodCallArray(nodep, adtypep)) { | |
| 4064 | newp->protect(false); | ||
| 4065 | newp->didWidth(true); | ||
| 4066 | ✗ | nodep->replaceWith(newp); | |
| 4067 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4068 | } else { | ||
| 4069 | ✗ | nodep->v3error("Unknown built-in array method " << nodep->prettyNameQ()); | |
| 4070 | nodep->dtypeFrom(adtypep->subDTypep()); // Best guess | ||
| 4071 | } | ||
| 4072 | } | ||
| 4073 | ✗ | void methodCallEvent(AstMethodCall* nodep, AstBasicDType*) { | |
| 4074 | // Method call on event | ||
| 4075 | ✗ | if (nodep->name() == "triggered") { | |
| 4076 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 4077 | AstCMethodHard* const callp = new AstCMethodHard{ | ||
| 4078 | ✗ | nodep->fileline(), nodep->fromp()->unlinkFrBack(), "isTriggered"}; | |
| 4079 | ✗ | callp->dtypeSetBit(); | |
| 4080 | ✗ | nodep->replaceWith(callp); | |
| 4081 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 4082 | } else { | ||
| 4083 | ✗ | nodep->v3error("Unknown built-in event method " << nodep->prettyNameQ()); | |
| 4084 | } | ||
| 4085 | } | ||
| 4086 | ✗ | void methodCallString(AstMethodCall* nodep, AstBasicDType*) { | |
| 4087 | // Method call on string | ||
| 4088 | ✗ | if (nodep->name() == "len") { | |
| 4089 | // Constant value | ||
| 4090 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 4091 | ✗ | AstNode* const newp = new AstLenN{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 4092 | ✗ | nodep->replaceWith(newp); | |
| 4093 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 4094 | ✗ | } else if (nodep->name() == "itoa") { | |
| 4095 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 4096 | ✗ | VL_DO_DANGLING(replaceWithSFormat(nodep, "%0d"), nodep); | |
| 4097 | ✗ | } else if (nodep->name() == "hextoa") { | |
| 4098 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 4099 | ✗ | VL_DO_DANGLING(replaceWithSFormat(nodep, "%0x"), nodep); | |
| 4100 | ✗ | } else if (nodep->name() == "octtoa") { | |
| 4101 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 4102 | ✗ | VL_DO_DANGLING(replaceWithSFormat(nodep, "%0o"), nodep); | |
| 4103 | ✗ | } else if (nodep->name() == "bintoa") { | |
| 4104 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 4105 | ✗ | VL_DO_DANGLING(replaceWithSFormat(nodep, "%0b"), nodep); | |
| 4106 | ✗ | } else if (nodep->name() == "realtoa") { | |
| 4107 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 4108 | ✗ | VL_DO_DANGLING(replaceWithSFormat(nodep, "%g"), nodep); | |
| 4109 | ✗ | } else if (nodep->name() == "tolower") { | |
| 4110 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 4111 | AstNode* const newp | ||
| 4112 | ✗ | = new AstToLowerN{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 4113 | ✗ | nodep->replaceWith(newp); | |
| 4114 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4115 | ✗ | } else if (nodep->name() == "toupper") { | |
| 4116 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 4117 | AstNode* const newp | ||
| 4118 | ✗ | = new AstToUpperN{nodep->fileline(), nodep->fromp()->unlinkFrBack()}; | |
| 4119 | ✗ | nodep->replaceWith(newp); | |
| 4120 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4121 | ✗ | } else if (nodep->name() == "compare" || nodep->name() == "icompare") { | |
| 4122 | ✗ | const bool ignoreCase = nodep->name()[0] == 'i'; | |
| 4123 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 4124 | ✗ | AstArg* const argp = VN_AS(nodep->pinsp(), Arg); | |
| 4125 | AstNodeExpr* const lhs = nodep->fromp()->unlinkFrBack(); | ||
| 4126 | AstNodeExpr* const rhs = argp->exprp()->unlinkFrBack(); | ||
| 4127 | ✗ | AstNode* const newp = new AstCompareNN{nodep->fileline(), lhs, rhs, ignoreCase}; | |
| 4128 | ✗ | nodep->replaceWith(newp); | |
| 4129 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4130 | ✗ | } else if (nodep->name() == "putc") { | |
| 4131 | ✗ | methodOkArguments(nodep, 2, 2); | |
| 4132 | ✗ | AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg); | |
| 4133 | ✗ | AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg); | |
| 4134 | ✗ | AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef); | |
| 4135 | AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack(); | ||
| 4136 | AstNodeExpr* const thsp = arg1p->exprp()->unlinkFrBack(); | ||
| 4137 | AstVarRef* const varrefp | ||
| 4138 | ✗ | = new AstVarRef{nodep->fileline(), fromp->varp(), VAccess::READ}; | |
| 4139 | AstNode* const newp = new AstAssign{ | ||
| 4140 | ✗ | nodep->fileline(), fromp, new AstPutcN{nodep->fileline(), varrefp, rhsp, thsp}}; | |
| 4141 | fromp->access(VAccess::WRITE); | ||
| 4142 | pushDeletep(nodep->backp()); | ||
| 4143 | ✗ | VL_DO_DANGLING(nodep->backp()->replaceWith(newp), nodep); | |
| 4144 | ✗ | } else if (nodep->name() == "getc") { | |
| 4145 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 4146 | ✗ | AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg); | |
| 4147 | AstNodeExpr* const lhsp = nodep->fromp()->unlinkFrBack(); | ||
| 4148 | AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack(); | ||
| 4149 | ✗ | AstNodeExpr* const newp = new AstGetcN{nodep->fileline(), lhsp, rhsp}; | |
| 4150 | ✗ | nodep->replaceWith(newp); | |
| 4151 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4152 | ✗ | } else if (nodep->name() == "substr") { | |
| 4153 | ✗ | methodOkArguments(nodep, 2, 2); | |
| 4154 | ✗ | AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg); | |
| 4155 | ✗ | AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg); | |
| 4156 | AstNodeExpr* const lhsp = nodep->fromp()->unlinkFrBack(); | ||
| 4157 | AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack(); | ||
| 4158 | AstNodeExpr* const thsp = arg1p->exprp()->unlinkFrBack(); | ||
| 4159 | ✗ | AstNodeExpr* const newp = new AstSubstrN{nodep->fileline(), lhsp, rhsp, thsp}; | |
| 4160 | ✗ | nodep->replaceWith(newp); | |
| 4161 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4162 | ✗ | } else if (nodep->name() == "atobin" || nodep->name() == "atohex" | |
| 4163 | ✗ | || nodep->name() == "atoi" || nodep->name() == "atooct" | |
| 4164 | ✗ | || nodep->name() == "atoreal") { | |
| 4165 | AstAtoN::FmtType fmt; | ||
| 4166 | ✗ | if (nodep->name() == "atobin") { | |
| 4167 | fmt = AstAtoN::ATOBIN; | ||
| 4168 | ✗ | } else if (nodep->name() == "atohex") { | |
| 4169 | fmt = AstAtoN::ATOHEX; | ||
| 4170 | ✗ | } else if (nodep->name() == "atoi") { | |
| 4171 | fmt = AstAtoN::ATOI; | ||
| 4172 | ✗ | } else if (nodep->name() == "atooct") { | |
| 4173 | fmt = AstAtoN::ATOOCT; | ||
| 4174 | ✗ | } else if (nodep->name() == "atoreal") { | |
| 4175 | fmt = AstAtoN::ATOREAL; | ||
| 4176 | } else { | ||
| 4177 | ✗ | V3ERROR_NA; | |
| 4178 | fmt = AstAtoN::ATOI; | ||
| 4179 | } // dummy assignment to suppress compiler warning | ||
| 4180 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 4181 | AstNode* const newp | ||
| 4182 | ✗ | = new AstAtoN{nodep->fileline(), nodep->fromp()->unlinkFrBack(), fmt}; | |
| 4183 | ✗ | nodep->replaceWith(newp); | |
| 4184 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 4185 | } else { | ||
| 4186 | ✗ | nodep->v3error("Unknown built-in string method " << nodep->prettyNameQ()); | |
| 4187 | } | ||
| 4188 | } | ||
| 4189 | ✗ | AstQueueDType* queueDTypeIndexedBy(AstNodeDType* indexDTypep) { | |
| 4190 | // Return a Queue data type with the specified index, remembering so can use again if | ||
| 4191 | // needed | ||
| 4192 | ✗ | if (AstQueueDType* const queuep = m_queueDTypeIndexed[indexDTypep]) { | |
| 4193 | return queuep; | ||
| 4194 | } else { | ||
| 4195 | ✗ | auto* const newp = new AstQueueDType{indexDTypep->fileline(), indexDTypep, nullptr}; | |
| 4196 | v3Global.rootp()->typeTablep()->addTypesp(newp); | ||
| 4197 | ✗ | m_queueDTypeIndexed[indexDTypep] = newp; | |
| 4198 | ✗ | return newp; | |
| 4199 | } | ||
| 4200 | } | ||
| 4201 | |||
| 4202 | ✗ | void visit(AstNew* nodep) override { | |
| 4203 | ✗ | if (nodep->didWidth()) return; | |
| 4204 | AstClass* classp = nullptr; | ||
| 4205 | bool assign = false; | ||
| 4206 | if (VN_IS(nodep->backp(), Assign)) { // assignment case | ||
| 4207 | assign = true; | ||
| 4208 | AstClassRefDType* const refp | ||
| 4209 | ✗ | = m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr; | |
| 4210 | if (!refp) { // e.g. int a = new; | ||
| 4211 | ✗ | nodep->v3error("new() assignment not legal to non-class data type " | |
| 4212 | + (m_vup->dtypeNullp() ? m_vup->dtypep()->prettyDTypeNameQ() : "")); | ||
| 4213 | ✗ | nodep->dtypep(m_vup->dtypep()); | |
| 4214 | ✗ | return; | |
| 4215 | } | ||
| 4216 | ✗ | nodep->dtypep(refp); | |
| 4217 | |||
| 4218 | classp = refp->classp(); | ||
| 4219 | ✗ | UASSERT_OBJ(classp, nodep, "Unlinked"); | |
| 4220 | ✗ | if (AstNodeFTask* const ftaskp | |
| 4221 | ✗ | = VN_CAST(m_memberMap.findMember(classp, "new"), Func)) { | |
| 4222 | nodep->taskp(ftaskp); | ||
| 4223 | nodep->classOrPackagep(classp); | ||
| 4224 | } else { | ||
| 4225 | // Either made explicitly or V3LinkDot made implicitly | ||
| 4226 | ✗ | classp->v3fatalSrc("Can't find class's new"); | |
| 4227 | } | ||
| 4228 | ✗ | if (classp->isVirtual() || classp->isInterfaceClass()) { | |
| 4229 | ✗ | nodep->v3error("Illegal to call 'new' using an abstract virtual class " | |
| 4230 | + AstNode::prettyNameQ(classp->origName()) | ||
| 4231 | + " (IEEE 1800-2023 8.21)"); | ||
| 4232 | } | ||
| 4233 | } else { // super.new case | ||
| 4234 | // in this case class and taskp() should be properly linked in V3LinkDot.cpp during | ||
| 4235 | // "super" reference resolution | ||
| 4236 | classp = VN_CAST(nodep->classOrPackagep(), Class); | ||
| 4237 | ✗ | UASSERT_OBJ(classp, nodep, "Unlinked classOrPackagep()"); | |
| 4238 | ✗ | UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked taskp()"); | |
| 4239 | } | ||
| 4240 | ✗ | processFTaskRefArgs(nodep); | |
| 4241 | ✗ | if (!assign) nodep->dtypeFrom(nodep->taskp()); | |
| 4242 | } | ||
| 4243 | ✗ | void visit(AstNewCopy* nodep) override { | |
| 4244 | ✗ | if (nodep->didWidthAndSet()) return; | |
| 4245 | ✗ | AstClassRefDType* const refp = VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType); | |
| 4246 | if (!refp) { // e.g. int a = new; | ||
| 4247 | ✗ | nodep->v3error("new() cannot copy from non-class data type " | |
| 4248 | + (m_vup->dtypeNullp() ? m_vup->dtypep()->prettyDTypeNameQ() : "")); | ||
| 4249 | ✗ | nodep->dtypep(m_vup->dtypep()); | |
| 4250 | ✗ | return; | |
| 4251 | } | ||
| 4252 | nodep->dtypep(refp); | ||
| 4253 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 4254 | ✗ | if (!similarDTypeRecurse(nodep->dtypep(), nodep->rhsp()->dtypep())) { | |
| 4255 | ✗ | nodep->rhsp()->v3error("New-as-copier passed different data type '" | |
| 4256 | << nodep->dtypep()->prettyTypeName() << "' then expected '" | ||
| 4257 | << nodep->rhsp()->dtypep()->prettyTypeName() << "'"); | ||
| 4258 | } | ||
| 4259 | } | ||
| 4260 | ✗ | void visit(AstNewDynamic* nodep) override { | |
| 4261 | ✗ | if (nodep->didWidthAndSet()) return; | |
| 4262 | ✗ | AstDynArrayDType* const adtypep = VN_CAST(m_vup->dtypeNullSkipRefp(), DynArrayDType); | |
| 4263 | if (!adtypep) { // e.g. int a = new; | ||
| 4264 | ✗ | nodep->v3error( | |
| 4265 | "dynamic new() not expected in this context (data type must be dynamic array)"); | ||
| 4266 | ✗ | return; | |
| 4267 | } | ||
| 4268 | // The AstNodeAssign visitor will be soon be replacing this node, make sure it gets it | ||
| 4269 | if (!VN_IS(nodep->backp(), NodeAssign)) { | ||
| 4270 | ✗ | UINFO(1, "Got backp " << nodep->backp() << endl); | |
| 4271 | ✗ | nodep->v3error( | |
| 4272 | "dynamic new() not expected in this context (expected under an assign)"); | ||
| 4273 | ✗ | return; | |
| 4274 | } | ||
| 4275 | nodep->dtypep(adtypep); | ||
| 4276 | ✗ | if (m_vup && m_vup->prelim()) { | |
| 4277 | ✗ | iterateCheckSigned32(nodep, "new() size", nodep->sizep(), BOTH); | |
| 4278 | } | ||
| 4279 | ✗ | if (nodep->rhsp()) { | |
| 4280 | ✗ | iterateCheckTyped(nodep, "Dynamic array new RHS", nodep->rhsp(), adtypep, BOTH); | |
| 4281 | } | ||
| 4282 | } | ||
| 4283 | |||
| 4284 | ✗ | void visit(AstPattern* nodep) override { | |
| 4285 | ✗ | if (nodep->didWidthAndSet()) return; | |
| 4286 | ✗ | UINFO(9, "PATTERN " << nodep << endl); | |
| 4287 | ✗ | if (nodep->childDTypep()) { // data_type '{ pattern } | |
| 4288 | ✗ | nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); | |
| 4289 | } | ||
| 4290 | ✗ | if (!nodep->dtypep() && m_vup->dtypeNullp()) { // Get it from parent assignment/pin/etc | |
| 4291 | ✗ | nodep->dtypep(m_vup->dtypep()); | |
| 4292 | } | ||
| 4293 | AstNodeDType* dtypep = nodep->dtypep(); | ||
| 4294 | ✗ | if (!dtypep) { | |
| 4295 | ✗ | nodep->v3warn(E_UNSUPPORTED, "Unsupported/Illegal: Assignment pattern" | |
| 4296 | " member not underneath a supported construct: " | ||
| 4297 | << nodep->backp()->prettyTypeName()); | ||
| 4298 | ✗ | return; | |
| 4299 | } | ||
| 4300 | { | ||
| 4301 | dtypep = dtypep->skipRefp(); | ||
| 4302 | nodep->dtypep(dtypep); | ||
| 4303 | ✗ | UINFO(9, " dtypep " << dtypep << endl); | |
| 4304 | nodep->dtypep(dtypep); | ||
| 4305 | // Determine replication count, and replicate initial value as | ||
| 4306 | // widths need to be individually determined | ||
| 4307 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; | |
| 4308 | ✗ | patp = VN_AS(patp->nextp(), PatMember)) { | |
| 4309 | ✗ | const int times = visitPatMemberRep(patp); | |
| 4310 | ✗ | for (int i = 1; i < times; i++) { | |
| 4311 | AstPatMember* const newp = patp->cloneTree(false); | ||
| 4312 | ✗ | patp->addNextHere(newp); | |
| 4313 | // This loop will see the new elements as part of nextp() | ||
| 4314 | } | ||
| 4315 | } | ||
| 4316 | // Convert any PatMember with multiple items to multiple PatMembers | ||
| 4317 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; | |
| 4318 | ✗ | patp = VN_AS(patp->nextp(), PatMember)) { | |
| 4319 | ✗ | if (patp->lhssp()->nextp()) { | |
| 4320 | // Can't just addNext, as would add to end of all members. | ||
| 4321 | // So detach, add next and reattach | ||
| 4322 | ✗ | VNRelinker relinkHandle; | |
| 4323 | patp->unlinkFrBack(&relinkHandle); | ||
| 4324 | ✗ | while (AstNodeExpr* const movep = VN_AS(patp->lhssp()->nextp(), NodeExpr)) { | |
| 4325 | movep->unlinkFrBack(); // Not unlinkFrBackWithNext, just one | ||
| 4326 | AstNode* newkeyp = nullptr; | ||
| 4327 | ✗ | if (patp->keyp()) newkeyp = patp->keyp()->cloneTree(true); | |
| 4328 | AstPatMember* const newp | ||
| 4329 | ✗ | = new AstPatMember{patp->fileline(), movep, newkeyp, nullptr}; | |
| 4330 | patp->addNext(newp); | ||
| 4331 | } | ||
| 4332 | relinkHandle.relink(patp); | ||
| 4333 | } | ||
| 4334 | } | ||
| 4335 | AstPatMember* defaultp = nullptr; | ||
| 4336 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;) { | |
| 4337 | ✗ | AstPatMember* const nextp = VN_AS(patp->nextp(), PatMember); | |
| 4338 | ✗ | if (patp->isDefault()) { | |
| 4339 | ✗ | if (defaultp) nodep->v3error("Multiple '{ default: } clauses"); | |
| 4340 | defaultp = patp; | ||
| 4341 | patp->unlinkFrBack(); | ||
| 4342 | } | ||
| 4343 | patp = nextp; | ||
| 4344 | } | ||
| 4345 | while (const AstConstDType* const vdtypep = VN_CAST(dtypep, ConstDType)) { | ||
| 4346 | dtypep = vdtypep->subDTypep()->skipRefp(); | ||
| 4347 | } | ||
| 4348 | |||
| 4349 | ✗ | userIterate(dtypep, WidthVP{SELF, BOTH}.p()); | |
| 4350 | |||
| 4351 | if (auto* const vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) { | ||
| 4352 | ✗ | VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep); | |
| 4353 | } else if (auto* const vdtypep = VN_CAST(dtypep, NodeArrayDType)) { | ||
| 4354 | ✗ | VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep); | |
| 4355 | } else if (auto* const vdtypep = VN_CAST(dtypep, AssocArrayDType)) { | ||
| 4356 | ✗ | VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep); | |
| 4357 | } else if (auto* const vdtypep = VN_CAST(dtypep, WildcardArrayDType)) { | ||
| 4358 | ✗ | VL_DO_DANGLING(patternWildcard(nodep, vdtypep, defaultp), nodep); | |
| 4359 | } else if (auto* const vdtypep = VN_CAST(dtypep, DynArrayDType)) { | ||
| 4360 | ✗ | VL_DO_DANGLING(patternDynArray(nodep, vdtypep, defaultp), nodep); | |
| 4361 | } else if (auto* const vdtypep = VN_CAST(dtypep, QueueDType)) { | ||
| 4362 | ✗ | VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep); | |
| 4363 | ✗ | } else if (VN_IS(dtypep, BasicDType) && VN_AS(dtypep, BasicDType)->isRanged()) { | |
| 4364 | ✗ | VL_DO_DANGLING(patternBasic(nodep, dtypep, defaultp), nodep); | |
| 4365 | } else { | ||
| 4366 | ✗ | nodep->v3warn( | |
| 4367 | E_UNSUPPORTED, | ||
| 4368 | "Unsupported: Assignment pattern applies against non struct/union data type: " | ||
| 4369 | << dtypep->prettyDTypeNameQ()); | ||
| 4370 | } | ||
| 4371 | } | ||
| 4372 | } | ||
| 4373 | ✗ | void patternUOrStruct(AstPattern* nodep, AstNodeUOrStructDType* vdtypep, | |
| 4374 | AstPatMember* defaultp) { | ||
| 4375 | // Due to "default" and tagged patterns, we need to determine | ||
| 4376 | // which member each AstPatMember corresponds to before we can | ||
| 4377 | // determine the dtypep for that PatMember's value, and then | ||
| 4378 | // width the initial value appropriately. | ||
| 4379 | using PatMap = std::map<const AstMemberDType*, AstPatMember*>; | ||
| 4380 | PatMap patmap; // Store member: value | ||
| 4381 | DTypeMap dtypemap; // Store data_type: default_value | ||
| 4382 | { | ||
| 4383 | ✗ | const AstMemberDType* memp = vdtypep->membersp(); | |
| 4384 | ✗ | AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); | |
| 4385 | ✗ | while (patp) { | |
| 4386 | do { | ||
| 4387 | ✗ | if (patp->keyp()) { | |
| 4388 | // '{member:value} or '{data_type: default_value} | ||
| 4389 | if (const AstText* textp = VN_CAST(patp->keyp(), Text)) { | ||
| 4390 | // member: value | ||
| 4391 | ✗ | memp = VN_CAST(m_memberMap.findMember(vdtypep, textp->text()), | |
| 4392 | MemberDType); | ||
| 4393 | ✗ | if (!memp) { | |
| 4394 | ✗ | patp->keyp()->v3error("Assignment pattern key '" | |
| 4395 | << textp->text() << "' not found as member"); | ||
| 4396 | ✗ | break; | |
| 4397 | } else { | ||
| 4398 | const std::pair<PatMap::iterator, bool> ret | ||
| 4399 | ✗ | = patmap.emplace(memp, patp); | |
| 4400 | ✗ | if (!ret.second) { | |
| 4401 | ✗ | patp->v3error("Assignment pattern contains duplicate entry: " | |
| 4402 | << VN_AS(patp->keyp(), Text)->text()); | ||
| 4403 | } | ||
| 4404 | ✗ | memp = VN_AS(memp->nextp(), MemberDType); | |
| 4405 | } | ||
| 4406 | } else if (AstNodeDType* nodedtypep = VN_CAST(patp->keyp(), NodeDType)) { | ||
| 4407 | // data_type: default_value | ||
| 4408 | ✗ | userIterate(nodedtypep, WidthVP{SELF, BOTH}.p()); | |
| 4409 | ✗ | const string dtype = nodedtypep->dtypep()->prettyDTypeName(true); | |
| 4410 | ✗ | const auto pair = dtypemap.emplace(dtype, patp); | |
| 4411 | ✗ | if (!pair.second) { | |
| 4412 | // Override stored default_value | ||
| 4413 | ✗ | pair.first->second = patp->cloneTree(false); | |
| 4414 | } | ||
| 4415 | } else { | ||
| 4416 | // Undefined pattern | ||
| 4417 | ✗ | patp->keyp()->v3error( | |
| 4418 | "Assignment pattern key not supported/understood: " | ||
| 4419 | << patp->keyp()->prettyTypeName()); | ||
| 4420 | } | ||
| 4421 | } else { | ||
| 4422 | // constant expr | ||
| 4423 | ✗ | if (memp) { | |
| 4424 | const std::pair<PatMap::iterator, bool> ret | ||
| 4425 | ✗ | = patmap.emplace(memp, patp); | |
| 4426 | ✗ | if (!ret.second) { | |
| 4427 | ✗ | patp->v3error("Assignment pattern contains duplicate entry: " | |
| 4428 | << VN_AS(patp->keyp(), Text)->text()); | ||
| 4429 | } | ||
| 4430 | ✗ | memp = VN_AS(memp->nextp(), MemberDType); | |
| 4431 | } | ||
| 4432 | } | ||
| 4433 | } while (false); | ||
| 4434 | |||
| 4435 | // Next | ||
| 4436 | ✗ | if (patp) patp = VN_AS(patp->nextp(), PatMember); | |
| 4437 | } | ||
| 4438 | } | ||
| 4439 | AstNodeExpr* newp = nullptr; | ||
| 4440 | ✗ | if (vdtypep->packed()) { | |
| 4441 | ✗ | for (AstMemberDType* memp = vdtypep->membersp(); memp; | |
| 4442 | ✗ | memp = VN_AS(memp->nextp(), MemberDType)) { | |
| 4443 | const auto it = patmap.find(memp); | ||
| 4444 | AstPatMember* patp = nullptr; | ||
| 4445 | ✗ | if (it == patmap.end()) { // default or default_type assignment | |
| 4446 | if (AstNodeUOrStructDType* const memp_nested_vdtypep | ||
| 4447 | = VN_CAST(memp->virtRefDTypep(), NodeUOrStructDType)) { | ||
| 4448 | ✗ | newp = nestedvalueConcat_patternUOrStruct(memp_nested_vdtypep, defaultp, | |
| 4449 | newp, nodep, dtypemap); | ||
| 4450 | } else { | ||
| 4451 | ✗ | patp = defaultPatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp, | |
| 4452 | dtypemap); | ||
| 4453 | ✗ | newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep); | |
| 4454 | } | ||
| 4455 | } else { // member assignment | ||
| 4456 | ✗ | patp = it->second; | |
| 4457 | ✗ | newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep); | |
| 4458 | } | ||
| 4459 | } | ||
| 4460 | } else { // Unpacked | ||
| 4461 | AstConsPackMember* membersp = nullptr; | ||
| 4462 | ✗ | for (AstMemberDType* memp = vdtypep->membersp(); memp; | |
| 4463 | ✗ | memp = VN_AS(memp->nextp(), MemberDType)) { | |
| 4464 | const auto it = patmap.find(memp); | ||
| 4465 | AstPatMember* patp = nullptr; | ||
| 4466 | ✗ | if (it == patmap.end()) { // Default or default_type assignment | |
| 4467 | ✗ | patp = defaultPatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp, | |
| 4468 | dtypemap); | ||
| 4469 | } else { | ||
| 4470 | ✗ | patp = it->second; // Member assignment | |
| 4471 | } | ||
| 4472 | patp->dtypep(memp); | ||
| 4473 | ✗ | AstNodeExpr* const valuep = patternMemberValueIterate(patp); | |
| 4474 | AstConsPackMember* const cpmp | ||
| 4475 | ✗ | = new AstConsPackMember{patp->fileline(), memp, valuep}; | |
| 4476 | ✗ | membersp = membersp ? membersp->addNext(cpmp) : cpmp; | |
| 4477 | } | ||
| 4478 | ✗ | newp = new AstConsPackUOrStruct{nodep->fileline(), vdtypep, membersp}; | |
| 4479 | } | ||
| 4480 | ✗ | if (newp) { | |
| 4481 | ✗ | nodep->replaceWith(newp); | |
| 4482 | } else { | ||
| 4483 | ✗ | nodep->v3error("Assignment pattern with no members"); | |
| 4484 | } | ||
| 4485 | VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present | ||
| 4486 | } | ||
| 4487 | |||
| 4488 | ✗ | AstNodeExpr* nestedvalueConcat_patternUOrStruct(AstNodeUOrStructDType* memp_vdtypep, | |
| 4489 | AstPatMember* defaultp, AstNodeExpr* newp, | ||
| 4490 | AstPattern* nodep, const DTypeMap& dtypemap) { | ||
| 4491 | AstPatMember* patp = nullptr; | ||
| 4492 | ✗ | for (AstMemberDType* memp_nested = memp_vdtypep->membersp(); memp_nested; | |
| 4493 | ✗ | memp_nested = VN_AS(memp_nested->nextp(), MemberDType)) { | |
| 4494 | if (AstNodeUOrStructDType* const memp_multinested_vdtypep | ||
| 4495 | = VN_CAST(memp_nested->virtRefDTypep(), NodeUOrStructDType)) { | ||
| 4496 | // When unpacked struct/union is supported this if will need some additional | ||
| 4497 | // conditions | ||
| 4498 | ✗ | newp = nestedvalueConcat_patternUOrStruct(memp_multinested_vdtypep, defaultp, newp, | |
| 4499 | nodep, dtypemap); | ||
| 4500 | } else { | ||
| 4501 | ✗ | patp = defaultPatp_patternUOrStruct(nodep, memp_nested, patp, memp_vdtypep, | |
| 4502 | defaultp, dtypemap); | ||
| 4503 | ✗ | newp = valueConcat_patternUOrStruct(patp, newp, memp_nested, nodep); | |
| 4504 | } | ||
| 4505 | } | ||
| 4506 | ✗ | return newp; | |
| 4507 | } | ||
| 4508 | |||
| 4509 | ✗ | AstPatMember* defaultPatp_patternUOrStruct(AstPattern* nodep, AstMemberDType* memp, | |
| 4510 | AstPatMember* patp, | ||
| 4511 | AstNodeUOrStructDType* memp_vdtypep, | ||
| 4512 | AstPatMember* defaultp, const DTypeMap& dtypemap) { | ||
| 4513 | ✗ | const string memp_DType = memp->virtRefDTypep()->prettyDTypeName(true); | |
| 4514 | const auto it = dtypemap.find(memp_DType); | ||
| 4515 | ✗ | if (it != dtypemap.end()) { | |
| 4516 | // default_value for data_type | ||
| 4517 | ✗ | patp = it->second->cloneTree(false); | |
| 4518 | ✗ | } else if (defaultp) { | |
| 4519 | // default_value for any unmatched member yet | ||
| 4520 | patp = defaultp->cloneTree(false); | ||
| 4521 | } else { | ||
| 4522 | if (!VN_IS(memp_vdtypep, UnionDType)) { | ||
| 4523 | ✗ | nodep->v3error("Assignment pattern missed initializing elements: " | |
| 4524 | << memp->virtRefDTypep()->prettyDTypeNameQ() << " " | ||
| 4525 | << memp->prettyNameQ()); | ||
| 4526 | } | ||
| 4527 | } | ||
| 4528 | ✗ | return patp; | |
| 4529 | } | ||
| 4530 | |||
| 4531 | ✗ | AstNodeExpr* valueConcat_patternUOrStruct(AstPatMember* patp, AstNodeExpr* newp, | |
| 4532 | AstMemberDType* memp, AstPattern* nodep) { | ||
| 4533 | ✗ | if (patp) { | |
| 4534 | patp->dtypep(memp); | ||
| 4535 | ✗ | AstNodeExpr* const valuep = patternMemberValueIterate(patp); | |
| 4536 | ✗ | if (!newp) { | |
| 4537 | newp = valuep; | ||
| 4538 | } else { | ||
| 4539 | ✗ | AstConcat* const concatp = new AstConcat{patp->fileline(), newp, valuep}; | |
| 4540 | newp = concatp; | ||
| 4541 | ✗ | newp->dtypeSetLogicSized(concatp->lhsp()->width() + concatp->rhsp()->width(), | |
| 4542 | nodep->dtypep()->numeric()); | ||
| 4543 | } | ||
| 4544 | } | ||
| 4545 | ✗ | return newp; | |
| 4546 | } | ||
| 4547 | |||
| 4548 | ✗ | void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) { | |
| 4549 | ✗ | const VNumRange range = arrayDtp->declRange(); | |
| 4550 | ✗ | PatVecMap patmap = patVectorMap(nodep, range); | |
| 4551 | ✗ | UINFO(9, "ent " << range.left() << " to " << range.right() << endl); | |
| 4552 | AstNode* newp = nullptr; | ||
| 4553 | bool allConstant = true; | ||
| 4554 | ✗ | const bool isConcat = nodep->itemsp() && VN_AS(nodep->itemsp(), PatMember)->isConcat(); | |
| 4555 | ✗ | for (int entn = 0, ent = range.left(); entn < range.elements(); | |
| 4556 | ✗ | ++entn, ent += range.leftToRightInc()) { | |
| 4557 | AstPatMember* newpatp = nullptr; | ||
| 4558 | AstPatMember* patp = nullptr; | ||
| 4559 | const auto it = patmap.find(ent); | ||
| 4560 | ✗ | if (it == patmap.end()) { | |
| 4561 | ✗ | if (defaultp) { | |
| 4562 | newpatp = defaultp->cloneTree(false); | ||
| 4563 | patp = newpatp; | ||
| 4564 | ✗ | } else if (!(VN_IS(arrayDtp, UnpackArrayDType) && !allConstant && isConcat)) { | |
| 4565 | // If arrayDtp is an unpacked array and item is not constant, | ||
| 4566 | // the number of elements cannot be determined here as the dtype of each | ||
| 4567 | // element is not set yet. V3Slice checks for such cases. | ||
| 4568 | ✗ | nodep->v3error("Assignment pattern missed initializing elements: " << ent); | |
| 4569 | } | ||
| 4570 | } else { | ||
| 4571 | ✗ | patp = it->second; | |
| 4572 | patmap.erase(it); | ||
| 4573 | } | ||
| 4574 | |||
| 4575 | ✗ | if (patp) { | |
| 4576 | // Don't want the RHS an array | ||
| 4577 | allConstant &= VN_IS(patp->lhssp(), Const); | ||
| 4578 | ✗ | patp->dtypep(arrayDtp->subDTypep()); | |
| 4579 | ✗ | AstNodeExpr* const valuep = patternMemberValueIterate(patp); | |
| 4580 | if (VN_IS(arrayDtp, UnpackArrayDType)) { | ||
| 4581 | ✗ | if (!newp) { | |
| 4582 | AstInitArray* const newap | ||
| 4583 | ✗ | = new AstInitArray{nodep->fileline(), arrayDtp, nullptr}; | |
| 4584 | newp = newap; | ||
| 4585 | } | ||
| 4586 | ✗ | VN_AS(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep); | |
| 4587 | } else { // Packed. Convert to concat for now. | ||
| 4588 | ✗ | if (!newp) { | |
| 4589 | newp = valuep; | ||
| 4590 | } else { | ||
| 4591 | AstConcat* const concatp | ||
| 4592 | ✗ | = new AstConcat{patp->fileline(), VN_AS(newp, NodeExpr), valuep}; | |
| 4593 | newp = concatp; | ||
| 4594 | ✗ | newp->dtypeSetLogicSized(concatp->lhsp()->width() | |
| 4595 | + concatp->rhsp()->width(), | ||
| 4596 | nodep->dtypep()->numeric()); | ||
| 4597 | } | ||
| 4598 | } | ||
| 4599 | } | ||
| 4600 | ✗ | if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp); | |
| 4601 | } | ||
| 4602 | ✗ | if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements"); | |
| 4603 | ✗ | if (newp) { | |
| 4604 | ✗ | nodep->replaceWith(newp); | |
| 4605 | } else { | ||
| 4606 | ✗ | nodep->v3error("Assignment pattern with no members"); | |
| 4607 | } | ||
| 4608 | // if (debug() >= 9) newp->dumpTree("- apat-out: "); | ||
| 4609 | VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present | ||
| 4610 | } | ||
| 4611 | ✗ | void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) { | |
| 4612 | AstNode* defaultValuep = nullptr; | ||
| 4613 | ✗ | if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack(); | |
| 4614 | ✗ | AstNodeExpr* newp = new AstConsAssoc{nodep->fileline(), defaultValuep}; | |
| 4615 | newp->dtypeFrom(arrayDtp); | ||
| 4616 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; | |
| 4617 | ✗ | patp = VN_AS(patp->nextp(), PatMember)) { | |
| 4618 | ✗ | patp->dtypep(arrayDtp->subDTypep()); | |
| 4619 | ✗ | AstNodeExpr* const valuep = patternMemberValueIterate(patp); | |
| 4620 | AstNode* keyp = patp->keyp(); | ||
| 4621 | ✗ | if (!keyp) { | |
| 4622 | ✗ | patp->v3error("Missing pattern key (need an expression then a ':')"); | |
| 4623 | ✗ | keyp = new AstConst{nodep->fileline(), AstConst::Signed32{}, 0}; | |
| 4624 | } else { | ||
| 4625 | ✗ | keyp->unlinkFrBack(); | |
| 4626 | } | ||
| 4627 | ✗ | AstSetAssoc* const newap = new AstSetAssoc{nodep->fileline(), newp, keyp, valuep}; | |
| 4628 | newap->dtypeFrom(arrayDtp); | ||
| 4629 | newp = newap; | ||
| 4630 | } | ||
| 4631 | ✗ | nodep->replaceWith(newp); | |
| 4632 | // if (debug() >= 9) newp->dumpTree("- apat-out: "); | ||
| 4633 | VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present | ||
| 4634 | } | ||
| 4635 | ✗ | void patternWildcard(AstPattern* nodep, AstWildcardArrayDType* arrayDtp, | |
| 4636 | AstPatMember* defaultp) { | ||
| 4637 | AstNode* defaultValuep = nullptr; | ||
| 4638 | ✗ | if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack(); | |
| 4639 | ✗ | AstNode* newp = new AstConsWildcard{nodep->fileline(), defaultValuep}; | |
| 4640 | newp->dtypeFrom(arrayDtp); | ||
| 4641 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; | |
| 4642 | ✗ | patp = VN_AS(patp->nextp(), PatMember)) { | |
| 4643 | patp->dtypep(arrayDtp->subDTypep()); | ||
| 4644 | ✗ | AstNodeExpr* const valuep = patternMemberValueIterate(patp); | |
| 4645 | AstNode* const keyp = patp->keyp(); | ||
| 4646 | AstSetWildcard* const newap | ||
| 4647 | ✗ | = new AstSetWildcard{nodep->fileline(), newp, keyp->unlinkFrBack(), valuep}; | |
| 4648 | newap->dtypeFrom(arrayDtp); | ||
| 4649 | newp = newap; | ||
| 4650 | } | ||
| 4651 | ✗ | nodep->replaceWith(newp); | |
| 4652 | // if (debug() >= 9) newp->dumpTree("- apat-out: "); | ||
| 4653 | VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present | ||
| 4654 | } | ||
| 4655 | ✗ | void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember*) { | |
| 4656 | ✗ | AstNode* newp = new AstConsDynArray{nodep->fileline()}; | |
| 4657 | newp->dtypeFrom(arrayp); | ||
| 4658 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; | |
| 4659 | ✗ | patp = VN_AS(patp->nextp(), PatMember)) { | |
| 4660 | patp->dtypep(arrayp->subDTypep()); | ||
| 4661 | ✗ | AstNodeExpr* const rhsp = patternMemberValueIterate(patp); | |
| 4662 | const bool rhsIsValue | ||
| 4663 | ✗ | = AstNode::computeCastable(rhsp->dtypep(), arrayp->subDTypep(), nullptr) | |
| 4664 | .isAssignable(); | ||
| 4665 | AstConsDynArray* const newap | ||
| 4666 | ✗ | = new AstConsDynArray{nodep->fileline(), rhsIsValue, rhsp, false, newp}; | |
| 4667 | newap->dtypeFrom(arrayp); | ||
| 4668 | newp = newap; | ||
| 4669 | } | ||
| 4670 | ✗ | nodep->replaceWith(newp); | |
| 4671 | // if (debug() >= 9) newp->dumpTree("- apat-out: "); | ||
| 4672 | VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present | ||
| 4673 | } | ||
| 4674 | ✗ | void patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember*) { | |
| 4675 | ✗ | AstNode* newp = new AstConsQueue{nodep->fileline()}; | |
| 4676 | newp->dtypeFrom(arrayp); | ||
| 4677 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; | |
| 4678 | ✗ | patp = VN_AS(patp->nextp(), PatMember)) { | |
| 4679 | patp->dtypep(arrayp->subDTypep()); | ||
| 4680 | ✗ | AstNodeExpr* const rhsp = patternMemberValueIterate(patp); | |
| 4681 | const bool rhsIsValue | ||
| 4682 | ✗ | = AstNode::computeCastable(rhsp->dtypep(), arrayp->subDTypep(), nullptr) | |
| 4683 | .isAssignable(); | ||
| 4684 | AstConsQueue* const newap | ||
| 4685 | ✗ | = new AstConsQueue{nodep->fileline(), rhsIsValue, rhsp, false, newp}; | |
| 4686 | newap->dtypeFrom(arrayp); | ||
| 4687 | newp = newap; | ||
| 4688 | } | ||
| 4689 | ✗ | nodep->replaceWith(newp); | |
| 4690 | // if (debug() >= 9) newp->dumpTree("- apat-out: "); | ||
| 4691 | VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present | ||
| 4692 | } | ||
| 4693 | ✗ | void patternBasic(AstPattern* nodep, AstNodeDType* vdtypep, AstPatMember* defaultp) { | |
| 4694 | ✗ | const AstBasicDType* bdtypep = VN_AS(vdtypep, BasicDType); | |
| 4695 | ✗ | const VNumRange range = bdtypep->declRange(); | |
| 4696 | ✗ | PatVecMap patmap = patVectorMap(nodep, range); | |
| 4697 | ✗ | UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl); | |
| 4698 | AstNodeExpr* newp = nullptr; | ||
| 4699 | ✗ | for (int ent = range.hi(); ent >= range.lo(); --ent) { | |
| 4700 | AstPatMember* newpatp = nullptr; | ||
| 4701 | AstPatMember* patp = nullptr; | ||
| 4702 | const auto it = patmap.find(ent); | ||
| 4703 | ✗ | if (it == patmap.end()) { | |
| 4704 | ✗ | if (defaultp) { | |
| 4705 | newpatp = defaultp->cloneTree(false); | ||
| 4706 | patp = newpatp; | ||
| 4707 | } else { | ||
| 4708 | ✗ | nodep->v3error("Assignment pattern missed initializing elements: " << ent); | |
| 4709 | } | ||
| 4710 | } else { | ||
| 4711 | ✗ | patp = it->second; | |
| 4712 | patmap.erase(it); | ||
| 4713 | } | ||
| 4714 | ✗ | if (patp) { | |
| 4715 | // Determine initial values | ||
| 4716 | ✗ | vdtypep = nodep->findBitDType(); | |
| 4717 | patp->dtypep(vdtypep); | ||
| 4718 | ✗ | AstNodeExpr* const valuep = patternMemberValueIterate(patp); | |
| 4719 | { // Packed. Convert to concat for now. | ||
| 4720 | ✗ | if (!newp) { | |
| 4721 | newp = valuep; | ||
| 4722 | } else { | ||
| 4723 | ✗ | AstConcat* const concatp = new AstConcat{patp->fileline(), newp, valuep}; | |
| 4724 | newp = concatp; | ||
| 4725 | ✗ | newp->dtypeSetLogicSized(concatp->lhsp()->width() | |
| 4726 | + concatp->rhsp()->width(), | ||
| 4727 | nodep->dtypep()->numeric()); | ||
| 4728 | } | ||
| 4729 | } | ||
| 4730 | } | ||
| 4731 | ✗ | if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp); | |
| 4732 | } | ||
| 4733 | ✗ | if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements"); | |
| 4734 | ✗ | if (newp) { | |
| 4735 | ✗ | nodep->replaceWith(newp); | |
| 4736 | } else { | ||
| 4737 | ✗ | nodep->v3error("Assignment pattern with no members"); | |
| 4738 | } | ||
| 4739 | // if (debug() >= 9) newp->dumpTree("- apat-out: "); | ||
| 4740 | VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present | ||
| 4741 | } | ||
| 4742 | ✗ | AstNodeExpr* patternMemberValueIterate(AstPatMember* patp) { | |
| 4743 | // Determine values - might be another InitArray | ||
| 4744 | ✗ | userIterate(patp, WidthVP{patp->dtypep(), BOTH}.p()); | |
| 4745 | // Convert to InitArray or constify immediately | ||
| 4746 | AstNodeExpr* valuep = patp->lhssp()->unlinkFrBack(); | ||
| 4747 | if (VN_IS(valuep, Const)) { | ||
| 4748 | // Forming a AstConcat will cause problems with | ||
| 4749 | // unsized (uncommitted sized) constants | ||
| 4750 | ✗ | if (AstConst* const newp = V3WidthCommit::newIfConstCommitSize(VN_AS(valuep, Const))) { | |
| 4751 | VL_DO_DANGLING(pushDeletep(valuep), valuep); | ||
| 4752 | valuep = newp; | ||
| 4753 | } | ||
| 4754 | } | ||
| 4755 | ✗ | return valuep; | |
| 4756 | } | ||
| 4757 | |||
| 4758 | ✗ | static void checkEventAssignment(const AstNodeAssign* const asgnp) { | |
| 4759 | string unsupEvtAsgn; | ||
| 4760 | ✗ | if (!usesDynamicScheduler(asgnp->lhsp())) unsupEvtAsgn = "to"; | |
| 4761 | ✗ | if (asgnp->rhsp()->dtypep()->isEvent() && !usesDynamicScheduler(asgnp->rhsp())) { | |
| 4762 | ✗ | unsupEvtAsgn += (unsupEvtAsgn.empty() ? "from" : " and from"); | |
| 4763 | } | ||
| 4764 | ✗ | if (!unsupEvtAsgn.empty()) { | |
| 4765 | ✗ | asgnp->v3warn(E_UNSUPPORTED, "Assignment " | |
| 4766 | << unsupEvtAsgn | ||
| 4767 | << " event in statically scheduled context.\n" | ||
| 4768 | << asgnp->warnMore() | ||
| 4769 | << "Static event " | ||
| 4770 | "scheduling won't be able to handle this.\n" | ||
| 4771 | << asgnp->warnMore() | ||
| 4772 | << "... Suggest move the event into a " | ||
| 4773 | "completely dynamic context, eg. a class, and " | ||
| 4774 | "reference it only from such context."); | ||
| 4775 | } | ||
| 4776 | } | ||
| 4777 | |||
| 4778 | ✗ | static bool usesDynamicScheduler(AstNode* nodep) { | |
| 4779 | ✗ | UASSERT_OBJ(nodep->dtypep()->isEvent(), nodep, "Node does not have an event dtype"); | |
| 4780 | while (true) { | ||
| 4781 | AstVarRef* const vrefp = VN_CAST(nodep, VarRef); | ||
| 4782 | if (vrefp) return usesDynamicScheduler(vrefp); | ||
| 4783 | if (VN_IS(nodep, MemberSel)) { | ||
| 4784 | return true; | ||
| 4785 | } else if (AstNodeSel* selp = VN_CAST(nodep, NodeSel)) { | ||
| 4786 | nodep = selp->fromp(); | ||
| 4787 | } else { | ||
| 4788 | return false; | ||
| 4789 | } | ||
| 4790 | } | ||
| 4791 | } | ||
| 4792 | |||
| 4793 | static bool usesDynamicScheduler(AstVarRef* vrefp) { | ||
| 4794 | return VN_IS(vrefp->classOrPackagep(), Class) || vrefp->varp()->isFuncLocal(); | ||
| 4795 | } | ||
| 4796 | |||
| 4797 | ✗ | void visit(AstPatMember* nodep) override { | |
| 4798 | ✗ | AstNodeDType* const vdtypep = m_vup->dtypeNullp(); | |
| 4799 | ✗ | UASSERT_OBJ(vdtypep, nodep, "Pattern member type not assigned by AstPattern visitor"); | |
| 4800 | ✗ | nodep->dtypep(vdtypep); | |
| 4801 | ✗ | UINFO(9, " PATMEMBER " << nodep << endl); | |
| 4802 | ✗ | UASSERT_OBJ(!nodep->lhssp()->nextp(), nodep, | |
| 4803 | "PatMember value should be singular w/replicates removed"); | ||
| 4804 | // Need to propagate assignment type downwards, even on prelim | ||
| 4805 | ✗ | userIterateChildren(nodep, WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 4806 | ✗ | iterateCheck(nodep, "Pattern value", nodep->lhssp(), ASSIGN, FINAL, vdtypep, EXTEND_LHS); | |
| 4807 | } | ||
| 4808 | ✗ | int visitPatMemberRep(AstPatMember* nodep) { | |
| 4809 | uint32_t times = 1; | ||
| 4810 | ✗ | if (nodep->repp()) { // else repp()==nullptr shorthand for rep count 1 | |
| 4811 | ✗ | iterateCheckSizedSelf(nodep, "LHS", nodep->repp(), SELF, BOTH); | |
| 4812 | V3Const::constifyParamsEdit(nodep->repp()); // repp may change | ||
| 4813 | const AstConst* const constp = VN_CAST(nodep->repp(), Const); | ||
| 4814 | if (!constp) { | ||
| 4815 | ✗ | nodep->v3error("Replication value isn't a constant."); | |
| 4816 | times = 0; | ||
| 4817 | } else { | ||
| 4818 | times = constp->toUInt(); | ||
| 4819 | } | ||
| 4820 | ✗ | if (times == 0) { | |
| 4821 | ✗ | nodep->v3error("Pattern replication value of 0 is not legal."); | |
| 4822 | times = 1; | ||
| 4823 | } | ||
| 4824 | nodep->repp() | ||
| 4825 | ->unlinkFrBackWithNext() | ||
| 4826 | ✗ | ->deleteTree(); // Done with replicate before cloning | |
| 4827 | } | ||
| 4828 | ✗ | return times; | |
| 4829 | } | ||
| 4830 | |||
| 4831 | ✗ | void visit(AstPropSpec* nodep) override { | |
| 4832 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 4833 | ✗ | iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); | |
| 4834 | ✗ | userIterateAndNext(nodep->sensesp(), nullptr); | |
| 4835 | ✗ | if (nodep->disablep()) { | |
| 4836 | ✗ | iterateCheckBool(nodep, "Disable", nodep->disablep(), | |
| 4837 | BOTH); // it's like an if() condition. | ||
| 4838 | } | ||
| 4839 | ✗ | nodep->dtypeSetBit(); | |
| 4840 | } | ||
| 4841 | } | ||
| 4842 | |||
| 4843 | //-------------------- | ||
| 4844 | // Top levels | ||
| 4845 | |||
| 4846 | ✗ | void visit(AstNodeCase* nodep) override { | |
| 4847 | // IEEE-2012 12.5: | ||
| 4848 | // Width: MAX(expr, all items) | ||
| 4849 | // Signed: Only if expr, and all items signed | ||
| 4850 | ✗ | assertAtStatement(nodep); | |
| 4851 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 4852 | ✗ | for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { | |
| 4853 | ✗ | nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced | |
| 4854 | ✗ | if (!VN_IS(nodep, GenCase)) userIterateAndNext(itemp->stmtsp(), nullptr); | |
| 4855 | ✗ | for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { | |
| 4856 | nextcp = condp->nextp(); // Prelim may cause the node to get replaced | ||
| 4857 | ✗ | VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp); | |
| 4858 | } | ||
| 4859 | } | ||
| 4860 | |||
| 4861 | // Deal with case(type(data_type)) | ||
| 4862 | if (AstAttrOf* const exprap = VN_CAST(nodep->exprp(), AttrOf)) { | ||
| 4863 | ✗ | if (exprap->attrType() == VAttrType::TYPEID) { | |
| 4864 | ✗ | AstNodeDType* const exprDtp = VN_AS(exprap->fromp(), NodeDType); | |
| 4865 | ✗ | UINFO(9, "case type exprDtp " << exprDtp << endl); | |
| 4866 | // V3Param may have a pointer to this case statement, and we need | ||
| 4867 | // dotted references to remain properly named, so rather than | ||
| 4868 | // removing we convert it to a "normal" expression "case (1) ..." | ||
| 4869 | FileLine* const newfl = nodep->fileline(); | ||
| 4870 | ✗ | newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform | |
| 4871 | ✗ | newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform | |
| 4872 | nodep->fileline(newfl); | ||
| 4873 | ✗ | for (AstCaseItem* itemp = nodep->itemsp(); itemp; | |
| 4874 | ✗ | itemp = VN_AS(itemp->nextp(), CaseItem)) { | |
| 4875 | ✗ | if (!itemp->isDefault()) { | |
| 4876 | bool hit = false; | ||
| 4877 | ✗ | for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { | |
| 4878 | const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf); | ||
| 4879 | if (!condAttrp) { | ||
| 4880 | ✗ | condp->v3error( | |
| 4881 | "Case(type) statement requires items that have type() items"); | ||
| 4882 | } else { | ||
| 4883 | ✗ | AstNodeDType* const condDtp = VN_AS(condAttrp->fromp(), NodeDType); | |
| 4884 | ✗ | if (AstNode::computeCastable(exprDtp, condDtp, nodep) | |
| 4885 | == VCastable::SAMEISH) { | ||
| 4886 | hit = true; | ||
| 4887 | break; | ||
| 4888 | } | ||
| 4889 | } | ||
| 4890 | } | ||
| 4891 | pushDeletep(itemp->condsp()->unlinkFrBackWithNext()); | ||
| 4892 | // Item condition becomes constant 1 if hits else 0 | ||
| 4893 | ✗ | itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit}); | |
| 4894 | } | ||
| 4895 | } | ||
| 4896 | VL_DO_DANGLING(pushDeletep(exprap->unlinkFrBack()), exprap); | ||
| 4897 | ✗ | nodep->exprp(new AstConst{newfl, AstConst::BitTrue{}}); | |
| 4898 | } | ||
| 4899 | } | ||
| 4900 | |||
| 4901 | // Take width as maximum across all items, if any is real whole thing is real | ||
| 4902 | AstNodeDType* subDTypep = nodep->exprp()->dtypep(); | ||
| 4903 | ✗ | for (AstCaseItem* itemp = nodep->itemsp(); itemp; | |
| 4904 | ✗ | itemp = VN_AS(itemp->nextp(), CaseItem)) { | |
| 4905 | ✗ | for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { | |
| 4906 | ✗ | if (condp->dtypep() != subDTypep) { | |
| 4907 | ✗ | if (condp->dtypep()->isDouble() || subDTypep->isDouble()) { | |
| 4908 | ✗ | subDTypep = nodep->findDoubleDType(); | |
| 4909 | ✗ | } else if (condp->dtypep()->isString() || subDTypep->isString()) { | |
| 4910 | ✗ | subDTypep = nodep->findStringDType(); | |
| 4911 | } else { | ||
| 4912 | ✗ | const int width = std::max(subDTypep->width(), condp->width()); | |
| 4913 | ✗ | const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin()); | |
| 4914 | ✗ | const bool issigned = subDTypep->isSigned() && condp->isSigned(); | |
| 4915 | subDTypep | ||
| 4916 | ✗ | = nodep->findLogicDType(width, mwidth, VSigning::fromBool(issigned)); | |
| 4917 | } | ||
| 4918 | } | ||
| 4919 | } | ||
| 4920 | } | ||
| 4921 | // Apply width | ||
| 4922 | ✗ | iterateCheck(nodep, "Case expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep, | |
| 4923 | EXTEND_LHS); | ||
| 4924 | ✗ | for (AstCaseItem* itemp = nodep->itemsp(); itemp; | |
| 4925 | ✗ | itemp = VN_AS(itemp->nextp(), CaseItem)) { | |
| 4926 | ✗ | for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { | |
| 4927 | nextcp = condp->nextp(); // Final may cause the node to get replaced | ||
| 4928 | ✗ | iterateCheck(nodep, "Case Item", condp, CONTEXT_DET, FINAL, subDTypep, EXTEND_LHS); | |
| 4929 | } | ||
| 4930 | } | ||
| 4931 | } | ||
| 4932 | ✗ | void visit(AstRandCase* nodep) override { | |
| 4933 | // IEEE says each item is a int (32-bits), and sizes are based on natural sizing, | ||
| 4934 | // but we'll sum to a 64-bit number then math is faster. | ||
| 4935 | ✗ | assertAtStatement(nodep); | |
| 4936 | v3Global.useRandomizeMethods(true); | ||
| 4937 | ✗ | AstNodeDType* const itemDTypep = nodep->findUInt32DType(); | |
| 4938 | ✗ | for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { | |
| 4939 | ✗ | nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced | |
| 4940 | ✗ | userIterateAndNext(itemp->stmtsp(), nullptr); | |
| 4941 | ✗ | for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { | |
| 4942 | nextcp = condp->nextp(); // Prelim may cause the node to get replaced | ||
| 4943 | ✗ | iterateCheckTyped(itemp, "Randcase Item", condp, itemDTypep, BOTH); | |
| 4944 | VL_DANGLING(condp); // Might have been replaced | ||
| 4945 | } | ||
| 4946 | VL_DANGLING(itemp); // Might have been replaced | ||
| 4947 | } | ||
| 4948 | } | ||
| 4949 | |||
| 4950 | ✗ | void visit(AstNodeFor* nodep) override { | |
| 4951 | ✗ | assertAtStatement(nodep); | |
| 4952 | ✗ | userIterateAndNext(nodep->initsp(), nullptr); | |
| 4953 | ✗ | iterateCheckBool(nodep, "For Test Condition", nodep->condp(), | |
| 4954 | BOTH); // it's like an if() condition. | ||
| 4955 | ✗ | if (!VN_IS(nodep, GenFor)) userIterateAndNext(nodep->stmtsp(), nullptr); | |
| 4956 | ✗ | userIterateAndNext(nodep->incsp(), nullptr); | |
| 4957 | } | ||
| 4958 | ✗ | void visit(AstRepeat* nodep) override { | |
| 4959 | ✗ | assertAtStatement(nodep); | |
| 4960 | ✗ | userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p()); | |
| 4961 | ✗ | userIterateAndNext(nodep->stmtsp(), nullptr); | |
| 4962 | } | ||
| 4963 | ✗ | void visit(AstWhile* nodep) override { | |
| 4964 | ✗ | assertAtStatement(nodep); | |
| 4965 | ✗ | userIterateAndNext(nodep->precondsp(), nullptr); | |
| 4966 | ✗ | iterateCheckBool(nodep, "For Test Condition", nodep->condp(), | |
| 4967 | BOTH); // it's like an if() condition. | ||
| 4968 | ✗ | userIterateAndNext(nodep->stmtsp(), nullptr); | |
| 4969 | ✗ | userIterateAndNext(nodep->incsp(), nullptr); | |
| 4970 | } | ||
| 4971 | 9079 | void visit(AstNodeIf* nodep) override { | |
| 4972 | 9079 | assertAtStatement(nodep); | |
| 4973 | // if (debug()) nodep->dumpTree("- IfPre: "); | ||
| 4974 | if (!VN_IS(nodep, GenIf)) { // for m_paramsOnly | ||
| 4975 | 9079 | userIterateAndNext(nodep->thensp(), nullptr); | |
| 4976 | 9079 | userIterateAndNext(nodep->elsesp(), nullptr); | |
| 4977 | } | ||
| 4978 | 9079 | iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition. | |
| 4979 | // if (debug()) nodep->dumpTree("- IfOut: "); | ||
| 4980 | 9079 | } | |
| 4981 | ✗ | void visit(AstExprStmt* nodep) override { | |
| 4982 | ✗ | userIterateAndNext(nodep->stmtsp(), nullptr); | |
| 4983 | // expected result is same as parent's expected result | ||
| 4984 | ✗ | userIterateAndNext(nodep->resultp(), m_vup); | |
| 4985 | nodep->dtypeFrom(nodep->resultp()); | ||
| 4986 | } | ||
| 4987 | ✗ | void visit(AstNodeForeach* nodep) override { | |
| 4988 | ✗ | if (nodep->didWidth()) return; | |
| 4989 | nodep->didWidth(true); | ||
| 4990 | const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars); | ||
| 4991 | ✗ | UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach"); | |
| 4992 | // if (debug()) nodep->dumpTree("- foreach-old: "); | ||
| 4993 | ✗ | userIterateAndNext(loopsp->fromp(), WidthVP{SELF, BOTH}.p()); | |
| 4994 | AstNodeExpr* const fromp = loopsp->fromp(); | ||
| 4995 | ✗ | UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type"); | |
| 4996 | AstNodeDType* fromDtp = fromp->dtypep()->skipRefp(); | ||
| 4997 | // Major dimension first | ||
| 4998 | ✗ | for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) { | |
| 4999 | next_argsp = argsp->nextp(); | ||
| 5000 | const bool empty = VN_IS(argsp, Empty); | ||
| 5001 | AstVar* const varp = VN_CAST(argsp, Var); | ||
| 5002 | ✗ | UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable"); | |
| 5003 | ✗ | if (varp) varp->usedLoopIdx(true); | |
| 5004 | ✗ | if (!fromDtp) { | |
| 5005 | ✗ | argsp->v3error("foreach loop variables exceed number of indices of array"); | |
| 5006 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 5007 | ✗ | return; | |
| 5008 | } | ||
| 5009 | fromDtp = fromDtp->skipRefp(); | ||
| 5010 | ✗ | UINFO(9, "- foreachArg " << argsp << endl); | |
| 5011 | ✗ | UINFO(9, "- from on " << fromp << endl); | |
| 5012 | ✗ | UINFO(9, "- from dtp " << fromDtp << endl); | |
| 5013 | |||
| 5014 | if (VN_IS(fromDtp, NodeArrayDType) || VN_IS(fromDtp, DynArrayDType) | ||
| 5015 | || VN_IS(fromDtp, QueueDType)) { | ||
| 5016 | // Nothing special here | ||
| 5017 | } else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) { | ||
| 5018 | ✗ | if (!adtypep->isString() && !adtypep->isRanged()) { | |
| 5019 | ✗ | argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName() | |
| 5020 | << " data type"); | ||
| 5021 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 5022 | ✗ | return; | |
| 5023 | } | ||
| 5024 | } else if (const AstAssocArrayDType* const adtypep | ||
| 5025 | = VN_CAST(fromDtp, AssocArrayDType)) { | ||
| 5026 | varp->dtypeFrom(adtypep->keyDTypep()); | ||
| 5027 | } else { | ||
| 5028 | ✗ | argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName() | |
| 5029 | << " data type"); | ||
| 5030 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 5031 | ✗ | return; | |
| 5032 | } | ||
| 5033 | ✗ | fromDtp = fromDtp->subDTypep(); | |
| 5034 | } | ||
| 5035 | // The parser validates we don't have "foreach (array[,,,])" | ||
| 5036 | AstNode* const bodyp = nodep->stmtsp(); | ||
| 5037 | ✗ | userIterateAndNext(bodyp, nullptr); | |
| 5038 | if (AstForeach* const loopp = VN_CAST(nodep, Foreach)) { | ||
| 5039 | ✗ | VL_DO_DANGLING(V3Begin::convertToWhile(loopp), nodep); | |
| 5040 | ✗ | return; | |
| 5041 | } | ||
| 5042 | } | ||
| 5043 | |||
| 5044 | 123115 | void visit(AstNodeAssign* nodep) override { | |
| 5045 | // IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5: (Careful of 11.8.1 which is | ||
| 5046 | // only one step; final dtype depends on assign LHS.) | ||
| 5047 | // Determine RHS type width and signing | ||
| 5048 | // Propagate type down to *non-self-determined* operators | ||
| 5049 | // Real propagates only across one operator if one side is real - | ||
| 5050 | // handled in each visitor. | ||
| 5051 | // Then LHS sign-extends only if *RHS* is signed | ||
| 5052 | 123115 | assertAtStatement(nodep); | |
| 5053 | // if (debug()) nodep->dumpTree("- AssignPre: "); | ||
| 5054 | { | ||
| 5055 | // if (debug()) nodep->dumpTree("- assin:: "); | ||
| 5056 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 123115 times.
|
123115 | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); |
| 5057 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123115 times.
|
123115 | UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LHS be untyped?"); |
| 5058 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123115 times.
|
123115 | UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LHS be unsized?"); |
| 5059 |
1/2✓ Branch 0 taken 123115 times.
✗ Branch 1 not taken.
|
123115 | nodep->dtypeFrom(nodep->lhsp()); |
| 5060 | // | ||
| 5061 | // AstPattern needs to know the proposed data type of the lhs, so pass on the prelim | ||
| 5062 | // if (debug()) nodep->dumpTree("- assrhs: "); | ||
| 5063 | 123115 | userIterateAndNext(nodep->rhsp(), WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 5064 | // | ||
| 5065 | // if (debug()) nodep->dumpTree("- assign: "); | ||
| 5066 | AstNodeDType* const lhsDTypep | ||
| 5067 | = nodep->lhsp()->dtypep(); // Note we use rhsp for context determined | ||
| 5068 | 123115 | iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep); | |
| 5069 | // if (debug()) nodep->dumpTree("- AssignOut: "); | ||
| 5070 | } | ||
| 5071 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123115 times.
|
123115 | if (auto* const controlp = nodep->timingControlp()) { |
| 5072 | ✗ | if (VN_IS(m_ftaskp, Func)) { | |
| 5073 | ✗ | controlp->v3error("Timing controls are not legal in functions. Suggest use a task " | |
| 5074 | "(IEEE 1800-2023 13.4.4)"); | ||
| 5075 | ✗ | VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp); | |
| 5076 | ✗ | } else if (nodep->fileline()->timingOn() && v3Global.opt.timing().isSetTrue()) { | |
| 5077 | iterateNull(controlp); | ||
| 5078 | } else { | ||
| 5079 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 5080 | ✗ | if (v3Global.opt.timing().isSetFalse()) { | |
| 5081 | ✗ | controlp->v3warn(ASSIGNDLY, "Ignoring timing control on this " | |
| 5082 | "assignment/primitive due to --no-timing"); | ||
| 5083 | } else { | ||
| 5084 | ✗ | controlp->v3warn(E_NEEDTIMINGOPT, | |
| 5085 | "Use --timing or --no-timing to specify how " | ||
| 5086 | "timing controls should be handled"); | ||
| 5087 | } | ||
| 5088 | } | ||
| 5089 | ✗ | VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp); | |
| 5090 | } | ||
| 5091 | } | ||
| 5092 | if (VN_IS(nodep->rhsp(), EmptyQueue)) { | ||
| 5093 | ✗ | UINFO(9, "= {} -> .delete(): " << nodep); | |
| 5094 | const AstNodeDType* const lhsDtp = nodep->lhsp()->dtypep()->skipRefp(); | ||
| 5095 | if (!VN_IS(lhsDtp, QueueDType) && !VN_IS(lhsDtp, DynArrayDType)) { | ||
| 5096 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 5097 | "Unsupported/Illegal: empty queue ('{}') in this assign context"); | ||
| 5098 | VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
| 5099 | ✗ | return; | |
| 5100 | } | ||
| 5101 | AstMethodCall* const newp = new AstMethodCall{ | ||
| 5102 | ✗ | nodep->fileline(), nodep->lhsp()->unlinkFrBack(), "delete", nullptr}; | |
| 5103 | ✗ | newp->dtypeSetVoid(); | |
| 5104 | ✗ | nodep->replaceWith(newp->makeStmt()); | |
| 5105 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 5106 | // Need to now convert it | ||
| 5107 | ✗ | visit(newp); | |
| 5108 | ✗ | return; | |
| 5109 | } | ||
| 5110 | if (const AstNewDynamic* const dynp = VN_CAST(nodep->rhsp(), NewDynamic)) { | ||
| 5111 | ✗ | UINFO(9, "= new[] -> .resize(): " << nodep); | |
| 5112 | AstCMethodHard* newp; | ||
| 5113 | ✗ | if (!dynp->rhsp()) { | |
| 5114 | newp = new AstCMethodHard{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), | ||
| 5115 | ✗ | "renew", dynp->sizep()->unlinkFrBack()}; | |
| 5116 | } else { | ||
| 5117 | newp = new AstCMethodHard{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), | ||
| 5118 | ✗ | "renew_copy", dynp->sizep()->unlinkFrBack()}; | |
| 5119 | newp->addPinsp(dynp->rhsp()->unlinkFrBack()); | ||
| 5120 | } | ||
| 5121 | newp->didWidth(true); | ||
| 5122 | newp->protect(false); | ||
| 5123 | ✗ | newp->dtypeSetVoid(); | |
| 5124 | ✗ | nodep->replaceWith(newp->makeStmt()); | |
| 5125 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 5126 | ✗ | return; | |
| 5127 | } | ||
| 5128 | |||
| 5129 | // Width check for unpacked array stream assignment | ||
| 5130 | if (const AstNodeStream* streamp = VN_CAST(nodep->rhsp(), NodeStream)) { | ||
| 5131 | if (AstUnpackArrayDType* arr = VN_CAST(streamp->lhsp()->dtypep(), UnpackArrayDType)) { | ||
| 5132 | int lwidth = nodep->lhsp()->width(); | ||
| 5133 | ✗ | int rwidth = arr->subDTypep()->width() * arr->arrayUnpackedElements(); | |
| 5134 | ✗ | if (lwidth != 0 && lwidth < rwidth) { | |
| 5135 | ✗ | nodep->v3widthWarn(lwidth, rwidth, | |
| 5136 | "Target fixed size variable (" | ||
| 5137 | << lwidth << " bits) is narrower than the stream (" | ||
| 5138 | << rwidth << " bits) (IEEE 1800-2023 11.4.14)"); | ||
| 5139 | } | ||
| 5140 | } | ||
| 5141 | } else if (const AstNodeStream* streamp = VN_CAST(nodep->lhsp(), NodeStream)) { | ||
| 5142 | if (AstUnpackArrayDType* arr = VN_CAST(streamp->lhsp()->dtypep(), UnpackArrayDType)) { | ||
| 5143 | int rwidth = nodep->rhsp()->width(); | ||
| 5144 | ✗ | int lwidth = arr->subDTypep()->width() * arr->arrayUnpackedElements(); | |
| 5145 | ✗ | if (rwidth != 0 && rwidth < lwidth) { | |
| 5146 | ✗ | nodep->v3widthWarn(lwidth, rwidth, | |
| 5147 | "Stream target requires " | ||
| 5148 | << lwidth | ||
| 5149 | << " bits, but source expression only provides " | ||
| 5150 | << rwidth << " bits (IEEE 1800-2023 11.4.14.3)"); | ||
| 5151 | } | ||
| 5152 | } | ||
| 5153 | } | ||
| 5154 | |||
| 5155 |
2/4✓ Branch 1 taken 123115 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 123115 times.
|
123115 | if (nodep->hasDType() && nodep->dtypep()->isEvent()) { |
| 5156 | ✗ | checkEventAssignment(nodep); | |
| 5157 | v3Global.setAssignsEvents(); | ||
| 5158 | } | ||
| 5159 | } | ||
| 5160 | |||
| 5161 | ✗ | void visit(AstRelease* nodep) override { | |
| 5162 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 5163 | ✗ | UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?"); | |
| 5164 | ✗ | UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?"); | |
| 5165 | } | ||
| 5166 | |||
| 5167 | ✗ | void formatNoStringArg(AstNode* argp, char ch) { | |
| 5168 | ✗ | if (argp && argp->isString()) { | |
| 5169 | ✗ | argp->v3error("$display-line format of '%"s + ch + "' illegal with string argument\n" | |
| 5170 | << argp->warnMore() << "... Suggest use '%s'"); | ||
| 5171 | } | ||
| 5172 | } | ||
| 5173 | |||
| 5174 | ✗ | void visit(AstSFormat* nodep) override { | |
| 5175 | ✗ | assertAtStatement(nodep); | |
| 5176 | ✗ | userIterateAndNext(nodep->fmtp(), WidthVP{SELF, BOTH}.p()); | |
| 5177 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 5178 | } | ||
| 5179 | ✗ | void visit(AstSFormatF* nodep) override { | |
| 5180 | // Excludes NodeDisplay, see below | ||
| 5181 | ✗ | if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function | |
| 5182 | // Just let all arguments seek their natural sizes | ||
| 5183 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 5184 | // | ||
| 5185 | ✗ | UINFO(9, " Display in " << nodep->text() << endl); | |
| 5186 | string newFormat; | ||
| 5187 | bool inPct = false; | ||
| 5188 | AstNodeExpr* argp = nodep->exprsp(); | ||
| 5189 | const string txt = nodep->text(); | ||
| 5190 | string fmt; | ||
| 5191 | ✗ | for (char ch : txt) { | |
| 5192 | ✗ | if (!inPct && ch == '%') { | |
| 5193 | inPct = true; | ||
| 5194 | fmt = ch; | ||
| 5195 | ✗ | } else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) { | |
| 5196 | ✗ | fmt += ch; | |
| 5197 | } else if (inPct) { | ||
| 5198 | inPct = false; | ||
| 5199 | bool added = false; | ||
| 5200 | ✗ | const AstNodeDType* const dtypep = argp ? argp->dtypep()->skipRefp() : nullptr; | |
| 5201 | ✗ | const AstBasicDType* const basicp = dtypep ? dtypep->basicp() : nullptr; | |
| 5202 | ✗ | ch = std::tolower(ch); | |
| 5203 | ✗ | if (ch == '?') { // Unspecified by user, guess | |
| 5204 | ✗ | if (argp && argp->isDouble()) { | |
| 5205 | ch = 'g'; | ||
| 5206 | ✗ | } else if (argp && argp->isString()) { | |
| 5207 | ch = '@'; | ||
| 5208 | ✗ | } else if (argp && nodep->missingArgChar() == 'd' && argp->isSigned()) { | |
| 5209 | ch = '~'; | ||
| 5210 | ✗ | } else if (basicp) { | |
| 5211 | ch = nodep->missingArgChar(); | ||
| 5212 | } else { | ||
| 5213 | ch = 'p'; | ||
| 5214 | } | ||
| 5215 | } | ||
| 5216 | ✗ | switch (ch) { | |
| 5217 | case '%': break; // %% - just output a % | ||
| 5218 | case 'm': break; // %m - auto insert "name" | ||
| 5219 | case 'l': break; // %m - auto insert "library" | ||
| 5220 | ✗ | case 'd': { // Convert decimal to either 'd' or '#' | |
| 5221 | ✗ | if (argp) { | |
| 5222 | ✗ | AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr); | |
| 5223 | ✗ | formatNoStringArg(argp, ch); | |
| 5224 | ✗ | if (argp->isDouble()) { | |
| 5225 | ✗ | spliceCvtS(argp, true, 64); | |
| 5226 | ch = '~'; | ||
| 5227 | } else if (argp->isSigned()) { // Convert it | ||
| 5228 | ch = '~'; | ||
| 5229 | } | ||
| 5230 | argp = nextp; | ||
| 5231 | } | ||
| 5232 | break; | ||
| 5233 | } | ||
| 5234 | ✗ | case 'b': // FALLTHRU | |
| 5235 | case 'o': // FALLTHRU | ||
| 5236 | case 'x': { | ||
| 5237 | ✗ | if (argp) { | |
| 5238 | ✗ | AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr); | |
| 5239 | ✗ | formatNoStringArg(argp, ch); | |
| 5240 | ✗ | if (argp->isDouble()) spliceCvtS(argp, true, 64); | |
| 5241 | argp = nextp; | ||
| 5242 | } | ||
| 5243 | break; | ||
| 5244 | } | ||
| 5245 | ✗ | case 'p': { // Pattern | |
| 5246 | ✗ | if (basicp && basicp->isString()) { | |
| 5247 | added = true; | ||
| 5248 | newFormat += "\"%@\""; | ||
| 5249 | ✗ | } else if (basicp && basicp->isDouble()) { | |
| 5250 | added = true; | ||
| 5251 | newFormat += "%g"; | ||
| 5252 | } else if (VN_IS(dtypep, AssocArrayDType) // | ||
| 5253 | || VN_IS(dtypep, WildcardArrayDType) // | ||
| 5254 | || VN_IS(dtypep, ClassRefDType) // | ||
| 5255 | || VN_IS(dtypep, DynArrayDType) // | ||
| 5256 | || VN_IS(dtypep, UnpackArrayDType) // | ||
| 5257 | || VN_IS(dtypep, QueueDType) | ||
| 5258 | || (VN_IS(dtypep, NodeUOrStructDType) | ||
| 5259 | ✗ | && !VN_AS(dtypep, NodeUOrStructDType)->packed())) { | |
| 5260 | added = true; | ||
| 5261 | newFormat += "%@"; | ||
| 5262 | ✗ | VNRelinker handle; | |
| 5263 | argp->unlinkFrBack(&handle); | ||
| 5264 | FileLine* const flp = nodep->fileline(); | ||
| 5265 | ✗ | AstCExpr* const newp = new AstCExpr{flp, nullptr}; | |
| 5266 | ✗ | newp->addExprsp(new AstText{flp, "VL_TO_STRING(", true}); | |
| 5267 | newp->addExprsp(argp); | ||
| 5268 | ✗ | newp->addExprsp(new AstText{flp, ")", true}); | |
| 5269 | ✗ | newp->dtypeSetString(); | |
| 5270 | newp->pure(true); | ||
| 5271 | newp->protect(false); | ||
| 5272 | handle.relink(newp); | ||
| 5273 | // Set argp to what we replaced it with, as we will keep processing the | ||
| 5274 | // next argument. | ||
| 5275 | argp = newp; | ||
| 5276 | } else { | ||
| 5277 | added = true; | ||
| 5278 | ✗ | if (fmt == "%0") { | |
| 5279 | newFormat += "'h%0h"; // IEEE our choice | ||
| 5280 | } else { | ||
| 5281 | newFormat += "%d"; | ||
| 5282 | } | ||
| 5283 | } | ||
| 5284 | ✗ | if (argp) argp = VN_AS(argp->nextp(), NodeExpr); | |
| 5285 | break; | ||
| 5286 | } | ||
| 5287 | ✗ | case 's': { // Convert string to pack string | |
| 5288 | ✗ | if (argp && argp->dtypep()->basicp()->isString()) { // Convert it | |
| 5289 | ch = '@'; | ||
| 5290 | } | ||
| 5291 | ✗ | if (argp) argp = VN_AS(argp->nextp(), NodeExpr); | |
| 5292 | break; | ||
| 5293 | } | ||
| 5294 | ✗ | case 't': { // Convert decimal time to realtime | |
| 5295 | ✗ | if (argp) { | |
| 5296 | ✗ | AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr); | |
| 5297 | ✗ | formatNoStringArg(argp, ch); | |
| 5298 | ✗ | if (argp->isDouble()) ch = '^'; // Convert it | |
| 5299 | ✗ | UASSERT_OBJ(!nodep->timeunit().isNone(), nodep, | |
| 5300 | "display %t has no time units"); | ||
| 5301 | argp = nextp; | ||
| 5302 | } | ||
| 5303 | break; | ||
| 5304 | } | ||
| 5305 | ✗ | case 'e': // FALLTHRU | |
| 5306 | case 'f': // FALLTHRU | ||
| 5307 | case 'g': { | ||
| 5308 | ✗ | if (argp) { | |
| 5309 | ✗ | AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr); | |
| 5310 | ✗ | formatNoStringArg(argp, ch); | |
| 5311 | ✗ | if (!argp->isDouble()) { | |
| 5312 | ✗ | iterateCheckReal(nodep, "Display argument", argp, BOTH); | |
| 5313 | } | ||
| 5314 | argp = nextp; | ||
| 5315 | } | ||
| 5316 | break; | ||
| 5317 | } | ||
| 5318 | ✗ | default: { // Most operators, just move to next argument | |
| 5319 | ✗ | if (argp) argp = VN_AS(argp->nextp(), NodeExpr); | |
| 5320 | break; | ||
| 5321 | } | ||
| 5322 | } // switch | ||
| 5323 | ✗ | if (!added) { | |
| 5324 | ✗ | fmt += ch; | |
| 5325 | newFormat += fmt; | ||
| 5326 | } | ||
| 5327 | } else { | ||
| 5328 | ✗ | newFormat += ch; | |
| 5329 | } | ||
| 5330 | } | ||
| 5331 | nodep->text(newFormat); | ||
| 5332 | ✗ | UINFO(9, " Display out " << nodep->text() << endl); | |
| 5333 | } | ||
| 5334 | ✗ | void visit(AstCReturn* nodep) override { | |
| 5335 | ✗ | assertAtStatement(nodep); | |
| 5336 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 5337 | } | ||
| 5338 | ✗ | void visit(AstConstraintRef* nodep) override { userIterateChildren(nodep, nullptr); } | |
| 5339 | ✗ | void visit(AstDisplay* nodep) override { | |
| 5340 | ✗ | assertAtStatement(nodep); | |
| 5341 | ✗ | if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5342 | // Just let all arguments seek their natural sizes | ||
| 5343 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 5344 | } | ||
| 5345 | ✗ | void visit(AstElabDisplay* nodep) override { | |
| 5346 | ✗ | assertAtStatement(nodep); | |
| 5347 | // Just let all arguments seek their natural sizes | ||
| 5348 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 5349 | ✗ | if (!m_paramsOnly) { | |
| 5350 | V3Const::constifyParamsEdit(nodep->fmtp()); // fmtp may change | ||
| 5351 | string text = nodep->fmtp()->text(); | ||
| 5352 | ✗ | if (text.empty()) text = "Elaboration system task message (IEEE 1800-2023 20.11)"; | |
| 5353 | ✗ | switch (nodep->displayType()) { | |
| 5354 | ✗ | case VDisplayType::DT_INFO: nodep->v3warn(USERINFO, text); break; | |
| 5355 | ✗ | case VDisplayType::DT_ERROR: nodep->v3warn(USERERROR, text); break; | |
| 5356 | ✗ | case VDisplayType::DT_WARNING: nodep->v3warn(USERWARN, text); break; | |
| 5357 | ✗ | case VDisplayType::DT_FATAL: nodep->v3warn(USERFATAL, text); break; | |
| 5358 | ✗ | default: UASSERT_OBJ(false, nodep, "Unexpected elaboration display type"); | |
| 5359 | } | ||
| 5360 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 5361 | } | ||
| 5362 | } | ||
| 5363 | ✗ | void visit(AstDumpCtl* nodep) override { | |
| 5364 | ✗ | assertAtStatement(nodep); | |
| 5365 | // Just let all arguments seek their natural sizes | ||
| 5366 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 5367 | } | ||
| 5368 | ✗ | void visit(AstFOpen* nodep) override { | |
| 5369 | // Although a system function in IEEE, here a statement which sets the file pointer (MCD) | ||
| 5370 | ✗ | userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p()); | |
| 5371 | ✗ | userIterateAndNext(nodep->modep(), WidthVP{SELF, BOTH}.p()); | |
| 5372 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5373 | } | ||
| 5374 | ✗ | void visit(AstFOpenMcd* nodep) override { | |
| 5375 | ✗ | userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p()); | |
| 5376 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5377 | } | ||
| 5378 | ✗ | void visit(AstFClose* nodep) override { | |
| 5379 | ✗ | assertAtStatement(nodep); | |
| 5380 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5381 | } | ||
| 5382 | ✗ | void visit(AstFError* nodep) override { | |
| 5383 | ✗ | if (m_vup->prelim()) { | |
| 5384 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5385 | // Could be string or packed array | ||
| 5386 | ✗ | userIterateAndNext(nodep->strp(), WidthVP{SELF, BOTH}.p()); | |
| 5387 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5388 | } | ||
| 5389 | } | ||
| 5390 | ✗ | void visit(AstFEof* nodep) override { | |
| 5391 | ✗ | if (m_vup->prelim()) { | |
| 5392 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5393 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5394 | } | ||
| 5395 | } | ||
| 5396 | ✗ | void visit(AstFFlush* nodep) override { | |
| 5397 | ✗ | assertAtStatement(nodep); | |
| 5398 | ✗ | if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5399 | } | ||
| 5400 | ✗ | void visit(AstFRewind* nodep) override { | |
| 5401 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5402 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5403 | } | ||
| 5404 | ✗ | void visit(AstFTell* nodep) override { | |
| 5405 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5406 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5407 | } | ||
| 5408 | ✗ | void visit(AstFSeek* nodep) override { | |
| 5409 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5410 | ✗ | iterateCheckSigned32(nodep, "$fseek offset", nodep->offset(), BOTH); | |
| 5411 | ✗ | iterateCheckSigned32(nodep, "$fseek operation", nodep->operation(), BOTH); | |
| 5412 | ✗ | nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5413 | } | ||
| 5414 | ✗ | void visit(AstFGetC* nodep) override { | |
| 5415 | ✗ | if (m_vup->prelim()) { | |
| 5416 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5417 | ✗ | nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED); // Spec says integer return | |
| 5418 | } | ||
| 5419 | } | ||
| 5420 | ✗ | void visit(AstFGetS* nodep) override { | |
| 5421 | ✗ | if (m_vup->prelim()) { | |
| 5422 | ✗ | nodep->dtypeSetSigned32(); // Spec says integer return | |
| 5423 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5424 | ✗ | userIterateAndNext(nodep->strgp(), WidthVP{SELF, BOTH}.p()); | |
| 5425 | } | ||
| 5426 | } | ||
| 5427 | ✗ | void visit(AstFUngetC* nodep) override { | |
| 5428 | ✗ | if (m_vup->prelim()) { | |
| 5429 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5430 | ✗ | iterateCheckSigned32(nodep, "$fungetc character", nodep->charp(), BOTH); | |
| 5431 | ✗ | nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED); // Spec says integer return | |
| 5432 | } | ||
| 5433 | } | ||
| 5434 | ✗ | void visit(AstFRead* nodep) override { | |
| 5435 | ✗ | if (m_vup->prelim()) { | |
| 5436 | ✗ | nodep->dtypeSetSigned32(); // Spec says integer return | |
| 5437 | ✗ | userIterateAndNext(nodep->memp(), WidthVP{SELF, BOTH}.p()); | |
| 5438 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5439 | ✗ | if (nodep->startp()) { | |
| 5440 | ✗ | iterateCheckSigned32(nodep, "$fread start", nodep->startp(), BOTH); | |
| 5441 | } | ||
| 5442 | ✗ | if (nodep->countp()) { | |
| 5443 | ✗ | iterateCheckSigned32(nodep, "$fread count", nodep->countp(), BOTH); | |
| 5444 | } | ||
| 5445 | } | ||
| 5446 | } | ||
| 5447 | ✗ | void visit(AstFScanF* nodep) override { | |
| 5448 | ✗ | if (m_vup->prelim()) { | |
| 5449 | ✗ | nodep->dtypeSetSigned32(); // Spec says integer return | |
| 5450 | ✗ | iterateCheckFileDesc(nodep, nodep->filep(), BOTH); | |
| 5451 | ✗ | userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p()); | |
| 5452 | } | ||
| 5453 | } | ||
| 5454 | ✗ | void visit(AstSScanF* nodep) override { | |
| 5455 | ✗ | if (m_vup->prelim()) { | |
| 5456 | ✗ | nodep->dtypeSetSigned32(); // Spec says integer return | |
| 5457 | ✗ | userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); | |
| 5458 | ✗ | userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p()); | |
| 5459 | } | ||
| 5460 | } | ||
| 5461 | ✗ | void visit(AstStackTraceF* nodep) override { nodep->dtypeSetString(); } | |
| 5462 | ✗ | void visit(AstSysIgnore* nodep) override { | |
| 5463 | ✗ | userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p()); | |
| 5464 | } | ||
| 5465 | ✗ | void visit(AstSystemF* nodep) override { | |
| 5466 | ✗ | if (m_vup->prelim()) { | |
| 5467 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 5468 | ✗ | nodep->dtypeSetSigned32(); // Spec says integer return | |
| 5469 | } | ||
| 5470 | } | ||
| 5471 | ✗ | void visit(AstSysFuncAsTask* nodep) override { | |
| 5472 | ✗ | assertAtStatement(nodep); | |
| 5473 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 5474 | } | ||
| 5475 | ✗ | void visit(AstSystemT* nodep) override { | |
| 5476 | ✗ | assertAtStatement(nodep); | |
| 5477 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 5478 | } | ||
| 5479 | ✗ | void visit(AstNodeReadWriteMem* nodep) override { | |
| 5480 | ✗ | assertAtStatement(nodep); | |
| 5481 | ✗ | userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p()); | |
| 5482 | ✗ | userIterateAndNext(nodep->memp(), WidthVP{SELF, BOTH}.p()); | |
| 5483 | const AstNodeDType* subp = nullptr; | ||
| 5484 | if (const AstAssocArrayDType* adtypep | ||
| 5485 | = VN_CAST(nodep->memp()->dtypep()->skipRefp(), AssocArrayDType)) { | ||
| 5486 | subp = adtypep->subDTypep(); | ||
| 5487 | ✗ | if (!adtypep->keyDTypep()->skipRefp()->basicp() | |
| 5488 | ✗ | || !adtypep->keyDTypep()->skipRefp()->basicp()->keyword().isIntNumeric()) { | |
| 5489 | ✗ | nodep->memp()->v3error(nodep->verilogKwd() | |
| 5490 | << " address/key must be integral (IEEE 1800-2023 21.4.1)"); | ||
| 5491 | } | ||
| 5492 | } else if (const AstUnpackArrayDType* const adtypep | ||
| 5493 | = VN_CAST(nodep->memp()->dtypep()->skipRefp(), UnpackArrayDType)) { | ||
| 5494 | subp = adtypep->subDTypep(); | ||
| 5495 | } else { | ||
| 5496 | ✗ | nodep->memp()->v3warn(E_UNSUPPORTED, | |
| 5497 | "Unsupported: " | ||
| 5498 | << nodep->verilogKwd() | ||
| 5499 | << " into other than unpacked or associative array"); | ||
| 5500 | } | ||
| 5501 | if (subp | ||
| 5502 | ✗ | && (!subp->skipRefp()->basicp() | |
| 5503 | ✗ | || !subp->skipRefp()->basicp()->keyword().isIntNumeric())) { | |
| 5504 | ✗ | nodep->memp()->v3warn(E_UNSUPPORTED, | |
| 5505 | "Unsupported: " << nodep->verilogKwd() | ||
| 5506 | << " array values must be integral"); | ||
| 5507 | } | ||
| 5508 | ✗ | userIterateAndNext(nodep->lsbp(), WidthVP{SELF, BOTH}.p()); | |
| 5509 | ✗ | userIterateAndNext(nodep->msbp(), WidthVP{SELF, BOTH}.p()); | |
| 5510 | } | ||
| 5511 | ✗ | void visit(AstTestPlusArgs* nodep) override { | |
| 5512 | ✗ | if (m_vup->prelim()) { | |
| 5513 | ✗ | userIterateAndNext(nodep->searchp(), WidthVP{SELF, BOTH}.p()); | |
| 5514 | ✗ | nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5515 | } | ||
| 5516 | } | ||
| 5517 | ✗ | void visit(AstValuePlusArgs* nodep) override { | |
| 5518 | ✗ | if (m_vup->prelim()) { | |
| 5519 | ✗ | userIterateAndNext(nodep->searchp(), WidthVP{SELF, BOTH}.p()); | |
| 5520 | ✗ | userIterateAndNext(nodep->outp(), WidthVP{SELF, BOTH}.p()); | |
| 5521 | ✗ | nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return | |
| 5522 | } | ||
| 5523 | } | ||
| 5524 | ✗ | void visit(AstTimeFormat* nodep) override { | |
| 5525 | ✗ | assertAtStatement(nodep); | |
| 5526 | ✗ | iterateCheckSigned32(nodep, "units", nodep->unitsp(), BOTH); | |
| 5527 | ✗ | iterateCheckSigned32(nodep, "precision", nodep->precisionp(), BOTH); | |
| 5528 | ✗ | iterateCheckString(nodep, "suffix", nodep->suffixp(), BOTH); | |
| 5529 | ✗ | iterateCheckSigned32(nodep, "width", nodep->widthp(), BOTH); | |
| 5530 | } | ||
| 5531 | ✗ | void visit(AstUCStmt* nodep) override { | |
| 5532 | // Just let all arguments seek their natural sizes | ||
| 5533 | ✗ | assertAtStatement(nodep); | |
| 5534 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 5535 | } | ||
| 5536 | ✗ | void visit(AstAssert* nodep) override { | |
| 5537 | ✗ | assertAtStatement(nodep); | |
| 5538 | ✗ | iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. | |
| 5539 | ✗ | userIterateAndNext(nodep->passsp(), nullptr); | |
| 5540 | ✗ | userIterateAndNext(nodep->failsp(), nullptr); | |
| 5541 | } | ||
| 5542 | ✗ | void visit(AstAssertCtl* nodep) override { | |
| 5543 | ✗ | assertAtStatement(nodep); | |
| 5544 | ✗ | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 5545 | } | ||
| 5546 | ✗ | void visit(AstAssertIntrinsic* nodep) override { | |
| 5547 | ✗ | assertAtStatement(nodep); | |
| 5548 | ✗ | iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. | |
| 5549 | ✗ | userIterateAndNext(nodep->passsp(), nullptr); | |
| 5550 | ✗ | userIterateAndNext(nodep->failsp(), nullptr); | |
| 5551 | } | ||
| 5552 | ✗ | void visit(AstCover* nodep) override { | |
| 5553 | ✗ | assertAtStatement(nodep); | |
| 5554 | ✗ | iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. | |
| 5555 | ✗ | userIterateAndNext(nodep->passsp(), nullptr); | |
| 5556 | } | ||
| 5557 | ✗ | void visit(AstRestrict* nodep) override { | |
| 5558 | ✗ | assertAtStatement(nodep); | |
| 5559 | ✗ | iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. | |
| 5560 | } | ||
| 5561 |
1/2✓ Branch 0 taken 13476 times.
✗ Branch 1 not taken.
|
13476 | void visit(AstPin* nodep) override { |
| 5562 | // if (debug()) nodep->dumpTree("- PinPre: "); | ||
| 5563 | // TOP LEVEL NODE | ||
| 5564 |
2/4✓ Branch 0 taken 13476 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13476 times.
|
13476 | if (nodep->modVarp() && nodep->modVarp()->isGParam()) { |
| 5565 | // Widthing handled as special init() case | ||
| 5566 | bool didWidth = false; | ||
| 5567 | if (auto* const patternp = VN_CAST(nodep->exprp(), Pattern)) { | ||
| 5568 | if (const AstVar* const modVarp = nodep->modVarp()) { | ||
| 5569 | // Convert BracketArrayDType | ||
| 5570 | userIterate(modVarp->childDTypep(), | ||
| 5571 | ✗ | WidthVP{SELF, BOTH}.p()); // May relink pointed to node | |
| 5572 | AstNodeDType* const setDtp = modVarp->childDTypep()->cloneTree(false); | ||
| 5573 | ✗ | if (!patternp->childDTypep()) patternp->childDTypep(setDtp); | |
| 5574 | ✗ | userIterateChildren(nodep, WidthVP{setDtp, BOTH}.p()); | |
| 5575 | didWidth = true; | ||
| 5576 | } | ||
| 5577 | } | ||
| 5578 | ✗ | if (!didWidth) userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); | |
| 5579 |
1/2✓ Branch 0 taken 13476 times.
✗ Branch 1 not taken.
|
13476 | } else if (!m_paramsOnly) { |
| 5580 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13476 times.
|
13476 | if (!nodep->modVarp()->didWidth()) { |
| 5581 | // Var hasn't been widthed, so make it so. | ||
| 5582 | userIterate(nodep->modVarp(), nullptr); | ||
| 5583 | } | ||
| 5584 |
1/2✓ Branch 0 taken 13476 times.
✗ Branch 1 not taken.
|
13476 | if (!nodep->exprp()) { // No-connect |
| 5585 | return; | ||
| 5586 | } | ||
| 5587 | // Very much like like an assignment, but which side is LH/RHS | ||
| 5588 | // depends on pin being a in/output/inout. | ||
| 5589 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 13476 times.
|
13476 | userIterateAndNext(nodep->exprp(), WidthVP{nodep->modVarp()->dtypep(), PRELIM}.p()); |
| 5590 | AstNodeDType* modDTypep = nodep->modVarp()->dtypep(); | ||
| 5591 | AstNodeDType* conDTypep = nodep->exprp()->dtypep(); | ||
| 5592 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13476 times.
|
13476 | UASSERT_OBJ(modDTypep, nodep, "Unlinked pin data type"); |
| 5593 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13476 times.
|
13476 | UASSERT_OBJ(conDTypep, nodep, "Unlinked pin data type"); |
| 5594 | modDTypep = modDTypep->skipRefp(); | ||
| 5595 | conDTypep = conDTypep->skipRefp(); | ||
| 5596 | AstNodeDType* subDTypep = modDTypep; | ||
| 5597 | const int modwidth = modDTypep->width(); | ||
| 5598 | const int conwidth = conDTypep->width(); | ||
| 5599 | if (conDTypep == modDTypep // If match, we're golden | ||
| 5600 |
3/4✓ Branch 0 taken 13476 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 9151 times.
✓ Branch 4 taken 4325 times.
|
13476 | || similarDTypeRecurse(conDTypep, modDTypep)) { |
| 5601 | 4325 | userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p()); | |
| 5602 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9151 times.
|
9151 | } else if (m_cellp->rangep()) { |
| 5603 | ✗ | const int numInsts = m_cellp->rangep()->elementsConst(); | |
| 5604 | ✗ | if (conwidth == modwidth) { | |
| 5605 | // Arrayed instants: widths match so connect to each instance | ||
| 5606 | subDTypep = conDTypep; // = same expr dtype | ||
| 5607 | ✗ | } else if (conwidth == numInsts * modwidth) { | |
| 5608 | // Arrayed instants: one bit for each of the instants (each | ||
| 5609 | // assign is 1 modwidth wide) | ||
| 5610 | subDTypep = conDTypep; // = same expr dtype (but numInst*pin_dtype) | ||
| 5611 | } else { | ||
| 5612 | // Must be a error according to spec | ||
| 5613 | // (Because we need to know if to connect to one or all instants) | ||
| 5614 | ✗ | nodep->v3error(ucfirst(nodep->prettyOperatorName()) | |
| 5615 | << " as part of a module instance array" | ||
| 5616 | << " requires " << modwidth << " or " << modwidth * numInsts | ||
| 5617 | << " bits, but connection's " | ||
| 5618 | << nodep->exprp()->prettyTypeName() << " generates " << conwidth | ||
| 5619 | << " bits. (IEEE 1800-2023 23.3.3)"); | ||
| 5620 | subDTypep = conDTypep; // = same expr dtype | ||
| 5621 | } | ||
| 5622 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p()); | |
| 5623 | } else { | ||
| 5624 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9151 times.
|
9151 | if (nodep->modVarp()->direction() == VDirection::REF) { |
| 5625 | ✗ | nodep->v3error("Ref connection " | |
| 5626 | << nodep->modVarp()->prettyNameQ() | ||
| 5627 | << " requires matching types;" | ||
| 5628 | << " ref requires " << modDTypep->prettyDTypeNameQ() | ||
| 5629 | << " data type but connection is " | ||
| 5630 | << conDTypep->prettyDTypeNameQ() << " data type."); | ||
| 5631 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9151 times.
|
9151 | } else if (nodep->modVarp()->isTristate()) { |
| 5632 | ✗ | if (modwidth != conwidth) { | |
| 5633 | // Ideally should call pinReconnectSimple which would tolerate this | ||
| 5634 | // then have a conversion warning | ||
| 5635 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 5636 | "Unsupported: " << ucfirst(nodep->prettyOperatorName()) | ||
| 5637 | << " to inout signal requires " << modwidth | ||
| 5638 | << " bits, but connection's " | ||
| 5639 | << nodep->exprp()->prettyTypeName() | ||
| 5640 | << " generates " << conwidth << " bits."); | ||
| 5641 | // otherwise would need some mess to force both sides to proper size | ||
| 5642 | } | ||
| 5643 | } else if (nodep->modVarp()->direction().isWritable() | ||
| 5644 |
3/6✓ Branch 0 taken 2076 times.
✓ Branch 1 taken 7075 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2076 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
9151 | && ((conDTypep->isDouble() && !modDTypep->isDouble()) |
| 5645 |
2/4✓ Branch 1 taken 2076 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2076 times.
|
2076 | || (!conDTypep->isDouble() && modDTypep->isDouble()))) { |
| 5646 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 5647 | "Unsupported: " << ucfirst(nodep->prettyOperatorName()) | ||
| 5648 | << " connects real to non-real"); | ||
| 5649 | } | ||
| 5650 | |||
| 5651 | // Check if an interface is connected to a non-interface and vice versa | ||
| 5652 | if ((VN_IS(modDTypep, IfaceRefDType) && !VN_IS(conDTypep, IfaceRefDType)) | ||
| 5653 | || (VN_IS(conDTypep, IfaceRefDType) && !VN_IS(modDTypep, IfaceRefDType))) { | ||
| 5654 | ✗ | nodep->v3error("Illegal " << nodep->prettyOperatorName() << "," | |
| 5655 | << " mismatch between port which is" | ||
| 5656 | << (VN_CAST(modDTypep, IfaceRefDType) ? "" : " not") | ||
| 5657 | << " an interface," | ||
| 5658 | << " and expression which is" | ||
| 5659 | << (VN_CAST(conDTypep, IfaceRefDType) ? "" : " not") | ||
| 5660 | << " an interface."); | ||
| 5661 | } | ||
| 5662 | |||
| 5663 | // TODO Simple dtype checking, should be a more general check | ||
| 5664 | const AstNodeArrayDType* const exprArrayp = VN_CAST(conDTypep, UnpackArrayDType); | ||
| 5665 | const AstNodeArrayDType* const modArrayp = VN_CAST(modDTypep, UnpackArrayDType); | ||
| 5666 | ✗ | if (exprArrayp && modArrayp && VN_IS(exprArrayp->subDTypep(), IfaceRefDType) | |
| 5667 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 9151 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
9151 | && exprArrayp->declRange().elements() != modArrayp->declRange().elements()) { |
| 5668 | ✗ | const int exprSize = exprArrayp->declRange().elements(); | |
| 5669 | ✗ | const int modSize = modArrayp->declRange().elements(); | |
| 5670 | ✗ | nodep->v3error("Illegal " | |
| 5671 | << nodep->prettyOperatorName() << "," | ||
| 5672 | << " mismatch between port which is an interface array of size " | ||
| 5673 | << modSize << "," | ||
| 5674 | << " and expression which is an interface array of size " | ||
| 5675 | << exprSize << "."); | ||
| 5676 | ✗ | UINFO(1, " Related lo: " << modDTypep << endl); | |
| 5677 | ✗ | UINFO(1, " Related hi: " << conDTypep << endl); | |
| 5678 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9151 times.
|
9151 | } else if ((exprArrayp && !modArrayp) || (!exprArrayp && modArrayp)) { |
| 5679 | ✗ | nodep->v3error("Illegal " << nodep->prettyOperatorName() << "," | |
| 5680 | << " mismatch between port which is" | ||
| 5681 | << (modArrayp ? "" : " not") << " an array," | ||
| 5682 | << " and expression which is" | ||
| 5683 | << (exprArrayp ? "" : " not") | ||
| 5684 | << " an array. (IEEE 1800-2023 7.6)"); | ||
| 5685 | ✗ | UINFO(1, " Related lo: " << modDTypep << endl); | |
| 5686 | ✗ | UINFO(1, " Related hi: " << conDTypep << endl); | |
| 5687 | } | ||
| 5688 | 9151 | iterateCheckAssign(nodep, "pin connection", nodep->exprp(), FINAL, subDTypep); | |
| 5689 | } | ||
| 5690 | } | ||
| 5691 | // if (debug()) nodep->dumpTree("- PinOut: "); | ||
| 5692 | } | ||
| 5693 | 4152 | void visit(AstCell* nodep) override { | |
| 5694 |
2/2✓ Branch 0 taken 2076 times.
✓ Branch 1 taken 2076 times.
|
4152 | VL_RESTORER(m_cellp); |
| 5695 | 4152 | m_cellp = nodep; | |
| 5696 |
2/2✓ Branch 0 taken 2076 times.
✓ Branch 1 taken 2076 times.
|
4152 | if (!m_paramsOnly) { |
| 5697 | if (VN_IS(nodep->modp(), NotFoundModule)) { | ||
| 5698 | // We've resolved parameters and hit a module that we couldn't resolve. It's | ||
| 5699 | // finally time to report it. | ||
| 5700 | // Note only here in V3Width as this is first visitor after V3Dead. | ||
| 5701 | ✗ | nodep->modNameFileline()->v3error("Cannot find file containing module: '" | |
| 5702 | << nodep->modName() << "'"); | ||
| 5703 | ✗ | v3Global.opt.filePathLookedMsg(nodep->modNameFileline(), nodep->modName()); | |
| 5704 | } | ||
| 5705 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 2076 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
2076 | if (nodep->rangep()) userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p()); |
| 5706 |
1/2✓ Branch 1 taken 2076 times.
✗ Branch 2 not taken.
|
2076 | userIterateAndNext(nodep->pinsp(), nullptr); |
| 5707 | } | ||
| 5708 |
1/2✓ Branch 1 taken 4152 times.
✗ Branch 2 not taken.
|
4152 | userIterateAndNext(nodep->paramsp(), nullptr); |
| 5709 | 4152 | } | |
| 5710 | ✗ | void visit(AstGatePin* nodep) override { | |
| 5711 | ✗ | if (m_vup->prelim()) { | |
| 5712 | ✗ | userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p()); | |
| 5713 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 5714 | ✗ | nodep->dtypeFrom(nodep->rangep()); | |
| 5715 | // Very much like like an pin | ||
| 5716 | const AstNodeDType* const conDTypep = nodep->exprp()->dtypep(); | ||
| 5717 | ✗ | const int numInsts = nodep->rangep()->elementsConst(); | |
| 5718 | const int modwidth = numInsts; | ||
| 5719 | const int conwidth = conDTypep->width(); | ||
| 5720 | ✗ | if (conwidth == 1 && modwidth > 1) { // Multiple connections | |
| 5721 | ✗ | AstNodeDType* const subDTypep = nodep->findLogicDType(1, 1, conDTypep->numeric()); | |
| 5722 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p()); | |
| 5723 | AstNode* const newp | ||
| 5724 | = new AstReplicate{nodep->fileline(), nodep->exprp()->unlinkFrBack(), | ||
| 5725 | ✗ | static_cast<uint32_t>(numInsts)}; | |
| 5726 | ✗ | nodep->replaceWith(newp); | |
| 5727 | } else { | ||
| 5728 | // Eliminating so pass down all of vup | ||
| 5729 | ✗ | userIterateAndNext(nodep->exprp(), m_vup); | |
| 5730 | ✗ | nodep->replaceWith(nodep->exprp()->unlinkFrBack()); | |
| 5731 | } | ||
| 5732 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 5733 | } | ||
| 5734 | } | ||
| 5735 | ✗ | void visit(AstNodeFTask* nodep) override { | |
| 5736 | // Grab width from the output variable (if it's a function) | ||
| 5737 | ✗ | if (nodep->didWidth()) return; | |
| 5738 | ✗ | if (nodep->doingWidth()) { | |
| 5739 | ✗ | UINFO(5, "Recursive function or task call: " << nodep); | |
| 5740 | nodep->recursive(true); | ||
| 5741 | nodep->didWidth(true); | ||
| 5742 | ✗ | return; | |
| 5743 | } | ||
| 5744 | ✗ | if (nodep->classMethod() && nodep->name() == "rand_mode") { | |
| 5745 | ✗ | nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden" | |
| 5746 | " (IEEE 1800-2023 18.8)"); | ||
| 5747 | ✗ | } else if (nodep->classMethod() && nodep->name() == "constraint_mode") { | |
| 5748 | ✗ | nodep->v3error("The 'constraint_mode' method is built-in and cannot be overridden" | |
| 5749 | " (IEEE 1800-2023 18.9)"); | ||
| 5750 | } | ||
| 5751 | // Function hasn't been widthed, so make it so. | ||
| 5752 | // Would use user1 etc, but V3Width called from too many places to spend a user | ||
| 5753 | nodep->doingWidth(true); | ||
| 5754 | ✗ | VL_RESTORER(m_funcp); | |
| 5755 | ✗ | VL_RESTORER(m_ftaskp); | |
| 5756 | ✗ | m_ftaskp = nodep; | |
| 5757 | // First width the function variable, as if is a recursive function we need data type | ||
| 5758 | ✗ | if (nodep->fvarp()) userIterate(nodep->fvarp(), nullptr); | |
| 5759 | ✗ | if (nodep->isConstructor()) { | |
| 5760 | // Pretend it's void so less special casing needed when look at dtypes | ||
| 5761 | ✗ | nodep->dtypeSetVoid(); | |
| 5762 | ✗ | } else if (nodep->fvarp()) { | |
| 5763 | ✗ | m_funcp = VN_AS(nodep, Func); | |
| 5764 | ✗ | UASSERT_OBJ(m_funcp, nodep, "FTask with function variable, but isn't a function"); | |
| 5765 | nodep->dtypeFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() | ||
| 5766 | } else if (VN_IS(nodep, Property)) { | ||
| 5767 | ✗ | nodep->dtypeSetBit(); | |
| 5768 | } | ||
| 5769 | WidthVP* vup = nullptr; | ||
| 5770 | ✗ | if (VN_IS(nodep, Property)) vup = WidthVP{SELF, BOTH}.p(); | |
| 5771 | userIterateChildren(nodep, vup); | ||
| 5772 | |||
| 5773 | nodep->didWidth(true); | ||
| 5774 | nodep->doingWidth(false); | ||
| 5775 | ✗ | if (nodep->dpiImport() && !nodep->dpiOpenParent() && markHasOpenArray(nodep)) { | |
| 5776 | nodep->dpiOpenParentInc(); // Mark so V3Task will wait for a child to build calling | ||
| 5777 | // func | ||
| 5778 | } | ||
| 5779 | } | ||
| 5780 | ✗ | void visit(AstConstraint* nodep) override { | |
| 5781 | ✗ | if (nodep->didWidth()) return; | |
| 5782 | ✗ | VL_RESTORER(m_constraintp); | |
| 5783 | ✗ | m_constraintp = nodep; | |
| 5784 | userIterateChildren(nodep, nullptr); | ||
| 5785 | nodep->didWidth(true); | ||
| 5786 | } | ||
| 5787 | ✗ | void visit(AstProperty* nodep) override { | |
| 5788 | ✗ | if (nodep->didWidth()) return; | |
| 5789 | ✗ | if (nodep->doingWidth()) { | |
| 5790 | ✗ | UINFO(5, "Recursive property call: " << nodep); | |
| 5791 | ✗ | nodep->v3warn(E_UNSUPPORTED, | |
| 5792 | "Unsupported: Recursive property call: " << nodep->prettyNameQ()); | ||
| 5793 | nodep->recursive(true); | ||
| 5794 | nodep->didWidth(true); | ||
| 5795 | ✗ | return; | |
| 5796 | } | ||
| 5797 | nodep->doingWidth(true); | ||
| 5798 | ✗ | m_ftaskp = nodep; | |
| 5799 | // Property call will be replaced by property body in V3AssertPre. Property body has bit | ||
| 5800 | // dtype, so set it here too | ||
| 5801 | ✗ | nodep->dtypeSetBit(); | |
| 5802 | ✗ | for (AstNode* propStmtp = nodep->stmtsp(); propStmtp; propStmtp = propStmtp->nextp()) { | |
| 5803 | if (VN_IS(propStmtp, Var)) { | ||
| 5804 | userIterate(propStmtp, nullptr); | ||
| 5805 | } else if (VN_IS(propStmtp, PropSpec)) { | ||
| 5806 | ✗ | iterateCheckSizedSelf(nodep, "PropSpec", propStmtp, SELF, BOTH); | |
| 5807 | } else { | ||
| 5808 | ✗ | propStmtp->v3fatal("Invalid statement under AstProperty"); | |
| 5809 | } | ||
| 5810 | } | ||
| 5811 | nodep->didWidth(true); | ||
| 5812 | nodep->doingWidth(false); | ||
| 5813 | ✗ | m_ftaskp = nullptr; | |
| 5814 | } | ||
| 5815 | ✗ | void visit(AstReturn* nodep) override { | |
| 5816 | // IEEE: Assignment-like context | ||
| 5817 | ✗ | assertAtStatement(nodep); | |
| 5818 | ✗ | if (!m_funcp) { | |
| 5819 | ✗ | if (nodep->lhsp()) { // Return w/o value ok other places | |
| 5820 | ✗ | nodep->v3error("Return with return value isn't underneath a function"); | |
| 5821 | } | ||
| 5822 | } else { | ||
| 5823 | ✗ | if (nodep->lhsp()) { | |
| 5824 | // Function hasn't been widthed, so make it so. | ||
| 5825 | nodep->dtypeFrom(m_funcp->fvarp()); | ||
| 5826 | // AstPattern requires assignments to pass datatype on PRELIM | ||
| 5827 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 5828 | ✗ | iterateCheckAssign(nodep, "Return value", nodep->lhsp(), FINAL, nodep->dtypep()); | |
| 5829 | } | ||
| 5830 | } | ||
| 5831 | } | ||
| 5832 | |||
| 5833 | AstPackage* getItemPackage(AstNode* pkgItemp) { | ||
| 5834 | ✗ | while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) { | |
| 5835 | pkgItemp = pkgItemp->backp(); | ||
| 5836 | } | ||
| 5837 | return VN_CAST(pkgItemp->backp(), Package); | ||
| 5838 | } | ||
| 5839 | ✗ | void visit(AstFuncRef* nodep) override { | |
| 5840 | ✗ | visit(static_cast<AstNodeFTaskRef*>(nodep)); | |
| 5841 | ✗ | if (nodep->taskp() && VN_IS(nodep->taskp(), Task)) { | |
| 5842 | ✗ | UASSERT_OBJ(m_vup, nodep, "Function reference where widthed expression expection"); | |
| 5843 | ✗ | if (m_vup->prelim()) | |
| 5844 | ✗ | nodep->v3error( | |
| 5845 | "Cannot call a task/void-function as a function: " << nodep->prettyNameQ()); | ||
| 5846 | ✗ | nodep->dtypeSetVoid(); | |
| 5847 | } else { | ||
| 5848 | nodep->dtypeFrom(nodep->taskp()); | ||
| 5849 | } | ||
| 5850 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 5851 | AstNodeModule* const classp = nodep->classOrPackagep(); | ||
| 5852 | ✗ | if (nodep->name() == "self" && classp->name() == "process") { | |
| 5853 | // Find if package the class is in is std:: | ||
| 5854 | AstPackage* const packagep = getItemPackage(classp); | ||
| 5855 | ✗ | if (packagep && packagep->name() == "std") { | |
| 5856 | ✗ | methodCallWarnTiming(nodep, "process"); | |
| 5857 | } | ||
| 5858 | } | ||
| 5859 | } | ||
| 5860 | // if (debug()) nodep->dumpTree("- FuncOut: "); | ||
| 5861 | } | ||
| 5862 | // Returns true if dtypep0 and dtypep1 have same dimensions | ||
| 5863 | ✗ | static bool areSameSize(AstUnpackArrayDType* dtypep0, AstUnpackArrayDType* dtypep1) { | |
| 5864 | ✗ | const std::vector<AstUnpackArrayDType*> dims0 = dtypep0->unpackDimensions(); | |
| 5865 | ✗ | const std::vector<AstUnpackArrayDType*> dims1 = dtypep1->unpackDimensions(); | |
| 5866 | ✗ | if (dims0.size() != dims1.size()) return false; | |
| 5867 | ✗ | for (size_t i = 0; i < dims0.size(); ++i) { | |
| 5868 | ✗ | if (dims0[i]->elementsConst() != dims1[i]->elementsConst()) return false; | |
| 5869 | } | ||
| 5870 | return true; | ||
| 5871 | } | ||
| 5872 | // Makes sure that port and pin have same size and same datatype | ||
| 5873 | ✗ | void checkUnpackedArrayArgs(AstVar* portp, AstNode* pinp) { | |
| 5874 | if (AstUnpackArrayDType* const portDtypep | ||
| 5875 | = VN_CAST(portp->dtypep()->skipRefp(), UnpackArrayDType)) { | ||
| 5876 | if (AstUnpackArrayDType* const pinDtypep | ||
| 5877 | = VN_CAST(pinp->dtypep()->skipRefp(), UnpackArrayDType)) { | ||
| 5878 | ✗ | if (!areSameSize(portDtypep, pinDtypep)) { | |
| 5879 | ✗ | pinp->v3warn(E_UNSUPPORTED, | |
| 5880 | "Shape of the argument does not match the shape of the parameter " | ||
| 5881 | << "(" << pinDtypep->prettyDTypeNameQ() << " v.s. " | ||
| 5882 | << portDtypep->prettyDTypeNameQ() << ")"); | ||
| 5883 | } | ||
| 5884 | ✗ | if (portDtypep->basicp()->width() != pinDtypep->basicp()->width() | |
| 5885 | ✗ | || (portDtypep->basicp()->keyword() != pinDtypep->basicp()->keyword() | |
| 5886 | ✗ | && !(portDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT | |
| 5887 | && pinDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC) | ||
| 5888 | ✗ | && !(portDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC | |
| 5889 | && pinDtypep->basicp()->keyword() | ||
| 5890 | == VBasicDTypeKwd::LOGIC_IMPLICIT))) { | ||
| 5891 | ✗ | pinp->v3warn(E_UNSUPPORTED, | |
| 5892 | "Shape of the argument does not match the shape of the parameter " | ||
| 5893 | << "(" << pinDtypep->basicp()->prettyDTypeNameQ() << " v.s. " | ||
| 5894 | << portDtypep->basicp()->prettyDTypeNameQ() << ")"); | ||
| 5895 | } | ||
| 5896 | } else { | ||
| 5897 | ✗ | pinp->v3warn(E_UNSUPPORTED, "Argument is not an unpacked array while parameter " | |
| 5898 | << portp->prettyNameQ() << " is"); | ||
| 5899 | } | ||
| 5900 | } | ||
| 5901 | } | ||
| 5902 | ✗ | void processFTaskRefArgs(AstNodeFTaskRef* nodep) { | |
| 5903 | // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign | ||
| 5904 | // Function hasn't been widthed, so make it so. | ||
| 5905 | ✗ | UINFO(5, " FTASKREF " << nodep << endl); | |
| 5906 | ✗ | UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked"); | |
| 5907 | ✗ | if (nodep->didWidth()) return; | |
| 5908 | userIterate(nodep->taskp(), nullptr); | ||
| 5909 | // | ||
| 5910 | // And do the arguments to the task/function too | ||
| 5911 | do { | ||
| 5912 | ✗ | reloop: | |
| 5913 | // taskConnects may create a new task, and change nodep->taskp() | ||
| 5914 | const V3TaskConnects tconnects | ||
| 5915 | ✗ | = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp(), &m_taskConnectState); | |
| 5916 | ✗ | if (m_taskConnectState.didWrap()) m_memberMap.clear(); // As added a member | |
| 5917 | ✗ | for (const auto& tconnect : tconnects) { | |
| 5918 | ✗ | const AstVar* const portp = tconnect.first; | |
| 5919 | ✗ | AstArg* const argp = tconnect.second; | |
| 5920 | AstNodeExpr* pinp = argp->exprp(); | ||
| 5921 | ✗ | if (!pinp) continue; // Argument error we'll find later | |
| 5922 | // Prelim may cause the node to get replaced; we've lost our | ||
| 5923 | // pointer, so need to iterate separately later | ||
| 5924 | if (portp->attrSFormat() | ||
| 5925 | ✗ | && (!VN_IS(pinp, SFormatF) || pinp->nextp())) { // Not already done | |
| 5926 | ✗ | UINFO(4, " sformat via metacomment: " << nodep << endl); | |
| 5927 | ✗ | VNRelinker handle; | |
| 5928 | argp->unlinkFrBackWithNext(&handle); // Format + additional args, if any | ||
| 5929 | AstNodeExpr* argsp = nullptr; | ||
| 5930 | ✗ | while (AstArg* const nextargp = VN_AS(argp->nextp(), Arg)) { | |
| 5931 | // Expression goes to SFormatF | ||
| 5932 | argsp = argsp->addNext(nextargp->exprp()->unlinkFrBackWithNext()); | ||
| 5933 | ✗ | nextargp->unlinkFrBack()->deleteTree(); // Remove the call's Arg wrapper | |
| 5934 | } | ||
| 5935 | string format; | ||
| 5936 | if (VN_IS(pinp, Const)) { | ||
| 5937 | ✗ | format = VN_AS(pinp, Const)->num().toString(); | |
| 5938 | } else { | ||
| 5939 | ✗ | pinp->v3error( | |
| 5940 | "Format to $display-like function must have constant format string"); | ||
| 5941 | } | ||
| 5942 | VL_DO_DANGLING(pushDeletep(argp), argp); | ||
| 5943 | AstSFormatF* const newp | ||
| 5944 | ✗ | = new AstSFormatF{nodep->fileline(), format, false, argsp}; | |
| 5945 | ✗ | if (!newp->scopeNamep() && newp->formatScopeTracking()) { | |
| 5946 | ✗ | newp->scopeNamep(new AstScopeName{newp->fileline(), true}); | |
| 5947 | } | ||
| 5948 | ✗ | handle.relink(new AstArg{newp->fileline(), "", newp}); | |
| 5949 | // Connection list is now incorrect (has extra args in it). | ||
| 5950 | ✗ | goto reloop; // so exit early; next loop will correct it | |
| 5951 | } // | ||
| 5952 | ✗ | else if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::STRING | |
| 5953 | && !VN_IS(pinp, CvtPackString) | ||
| 5954 | && !VN_IS(pinp, SFormatF) // Already generates a string | ||
| 5955 | && !VN_IS(portp->dtypep(), UnpackArrayDType) // Unpacked array must match | ||
| 5956 | ✗ | && !(VN_IS(pinp, VarRef) | |
| 5957 | ✗ | && VN_AS(pinp, VarRef)->varp()->basicp()->keyword() | |
| 5958 | == VBasicDTypeKwd::STRING)) { | ||
| 5959 | ✗ | UINFO(4, " Add CvtPackString: " << pinp << endl); | |
| 5960 | ✗ | VNRelinker handle; | |
| 5961 | pinp->unlinkFrBack(&handle); // No next, that's the next pin | ||
| 5962 | ✗ | AstNodeExpr* const newp = new AstCvtPackString{pinp->fileline(), pinp}; | |
| 5963 | handle.relink(newp); | ||
| 5964 | pinp = newp; | ||
| 5965 | } | ||
| 5966 | // AstPattern requires assignments to pass datatype on PRELIM | ||
| 5967 | ✗ | VL_DO_DANGLING(userIterate(pinp, WidthVP{portp->dtypep(), PRELIM}.p()), pinp); | |
| 5968 | } | ||
| 5969 | } while (false); | ||
| 5970 | // Stage 2 | ||
| 5971 | { | ||
| 5972 | ✗ | const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); | |
| 5973 | ✗ | for (const auto& tconnect : tconnects) { | |
| 5974 | ✗ | AstVar* const portp = tconnect.first; | |
| 5975 | ✗ | const AstArg* const argp = tconnect.second; | |
| 5976 | AstNodeExpr* const pinp = argp->exprp(); | ||
| 5977 | ✗ | if (!pinp) continue; // Argument error we'll find later | |
| 5978 | // Change data types based on above accept completion | ||
| 5979 | ✗ | if (nodep->taskp()->dpiImport()) checkUnpackedArrayArgs(portp, pinp); | |
| 5980 | ✗ | if (portp->isDouble()) VL_DO_DANGLING(spliceCvtD(pinp), pinp); | |
| 5981 | } | ||
| 5982 | } | ||
| 5983 | // Stage 3 | ||
| 5984 | { | ||
| 5985 | ✗ | const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); | |
| 5986 | ✗ | for (const auto& tconnect : tconnects) { | |
| 5987 | ✗ | const AstVar* const portp = tconnect.first; | |
| 5988 | ✗ | const AstArg* const argp = tconnect.second; | |
| 5989 | AstNodeExpr* const pinp = argp->exprp(); | ||
| 5990 | ✗ | if (!pinp) continue; // Argument error we'll find later | |
| 5991 | // Do PRELIM again, because above accept may have exited early | ||
| 5992 | // due to node replacement | ||
| 5993 | ✗ | userIterate(pinp, WidthVP{portp->dtypep(), PRELIM}.p()); | |
| 5994 | } | ||
| 5995 | } | ||
| 5996 | // Cleanup any open arrays | ||
| 5997 | ✗ | if (markHasOpenArray(nodep->taskp())) makeOpenArrayShell(nodep); | |
| 5998 | // Stage 4 | ||
| 5999 | { | ||
| 6000 | ✗ | const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); | |
| 6001 | ✗ | for (const auto& tconnect : tconnects) { | |
| 6002 | ✗ | const AstVar* const portp = tconnect.first; | |
| 6003 | ✗ | const AstArg* const argp = tconnect.second; | |
| 6004 | AstNodeExpr* const pinp = argp->exprp(); | ||
| 6005 | ✗ | if (!pinp) continue; // Argument error we'll find later | |
| 6006 | AstNodeDType* const portDTypep = portp->dtypep()->skipRefToEnump(); | ||
| 6007 | const AstNodeDType* const pinDTypep = pinp->dtypep()->skipRefToEnump(); | ||
| 6008 | if (portp->direction() == VDirection::REF | ||
| 6009 | ✗ | && !similarDTypeRecurse(portDTypep, pinDTypep)) { | |
| 6010 | ✗ | pinp->v3error("Ref argument requires matching types;" | |
| 6011 | << " port " << portp->prettyNameQ() << " requires " | ||
| 6012 | << portDTypep->prettyDTypeNameQ() << " but connection is " | ||
| 6013 | << pinDTypep->prettyDTypeNameQ() << "."); | ||
| 6014 | ✗ | } else if (portp->isWritable() && pinp->width() != portp->width()) { | |
| 6015 | ✗ | pinp->v3widthWarn(portp->width(), pinp->width(), | |
| 6016 | "Function output argument " | ||
| 6017 | << portp->prettyNameQ() << " requires " << portp->width() | ||
| 6018 | << " bits, but connection's " << pinp->prettyTypeName() | ||
| 6019 | << " generates " << pinp->width() << " bits."); | ||
| 6020 | ✗ | VNRelinker relinkHandle; | |
| 6021 | pinp->unlinkFrBack(&relinkHandle); | ||
| 6022 | ✗ | AstNodeExpr* const newp = new AstResizeLValue{pinp->fileline(), pinp}; | |
| 6023 | relinkHandle.relink(newp); | ||
| 6024 | } | ||
| 6025 | ✗ | if (portp->isWritable()) V3LinkLValue::linkLValueSet(pinp); | |
| 6026 | ✗ | if (!portp->basicp() || portp->basicp()->isOpaque()) { | |
| 6027 | ✗ | checkClassAssign(nodep, "Function Argument", pinp, portDTypep); | |
| 6028 | ✗ | userIterate(pinp, WidthVP{portDTypep, FINAL}.p()); | |
| 6029 | } else { | ||
| 6030 | ✗ | iterateCheckAssign(nodep, "Function Argument", pinp, FINAL, portDTypep); | |
| 6031 | } | ||
| 6032 | } | ||
| 6033 | } | ||
| 6034 | } | ||
| 6035 | ✗ | void visit(AstNodeFTaskRef* nodep) override { | |
| 6036 | // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign | ||
| 6037 | // Function hasn't been widthed, so make it so. | ||
| 6038 | ✗ | UINFO(5, " FTASKREF " << nodep << endl); | |
| 6039 | AstWith* withp = nullptr; | ||
| 6040 | ✗ | if (nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") { | |
| 6041 | v3Global.useRandomizeMethods(true); | ||
| 6042 | ✗ | nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT)); | |
| 6043 | ✗ | return; // Handled in V3Randomize | |
| 6044 | ✗ | } else if (nodep->name() == "randomize" || nodep->name() == "srandom" | |
| 6045 | ✗ | || (!nodep->taskp() | |
| 6046 | ✗ | && (nodep->name() == "get_randstate" | |
| 6047 | ✗ | || nodep->name() == "set_randstate"))) { | |
| 6048 | // TODO perhaps this should move to V3LinkDot | ||
| 6049 | AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class); | ||
| 6050 | if (!classp) { | ||
| 6051 | ✗ | AstNodeDType* const adtypep = nodep->findBitDType(); | |
| 6052 | ✗ | withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(), | |
| 6053 | adtypep->findBitDType(), adtypep); | ||
| 6054 | ✗ | for (const AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) | |
| 6055 | ✗ | userIterateAndNext(VN_AS(argp, Arg)->exprp(), WidthVP{SELF, BOTH}.p()); | |
| 6056 | nodep->addPinsp(withp); | ||
| 6057 | ✗ | nodep->v3warn(CONSTRAINTIGN, "std::randomize ignored (unsupported)"); | |
| 6058 | ✗ | nodep->replaceWith(new AstConst{nodep->fileline(), 0}); | |
| 6059 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 6060 | ✗ | return; | |
| 6061 | } | ||
| 6062 | UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot"); | ||
| 6063 | ✗ | if (nodep->name() == "randomize") { | |
| 6064 | AstClassRefDType* const adtypep | ||
| 6065 | ✗ | = new AstClassRefDType{nodep->fileline(), classp, nullptr}; | |
| 6066 | v3Global.rootp()->typeTablep()->addTypesp(adtypep); | ||
| 6067 | ✗ | withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(), | |
| 6068 | adtypep->findBitDType(), adtypep); | ||
| 6069 | ✗ | handleRandomizeArgs(nodep, classp); | |
| 6070 | ✗ | } else if (nodep->name() == "srandom") { | |
| 6071 | ✗ | nodep->taskp(V3Randomize::newSRandomFunc(m_memberMap, classp)); | |
| 6072 | m_memberMap.clear(); | ||
| 6073 | ✗ | } else if (nodep->name() == "get_randstate") { | |
| 6074 | ✗ | methodOkArguments(nodep, 0, 0); | |
| 6075 | ✗ | classp->baseMostClassp()->needRNG(true); | |
| 6076 | v3Global.useRandomizeMethods(true); | ||
| 6077 | AstCExpr* const newp | ||
| 6078 | ✗ | = new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1, true}; | |
| 6079 | ✗ | newp->dtypeSetString(); | |
| 6080 | ✗ | nodep->replaceWith(newp); | |
| 6081 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 6082 | ✗ | return; | |
| 6083 | ✗ | } else if (nodep->name() == "set_randstate") { | |
| 6084 | ✗ | methodOkArguments(nodep, 1, 1); | |
| 6085 | ✗ | AstNodeExpr* const expr1p = VN_AS(nodep->pinsp(), Arg)->exprp(); // May edit | |
| 6086 | ✗ | iterateCheckString(nodep, "LHS", expr1p, BOTH); | |
| 6087 | ✗ | AstNodeExpr* const exprp = VN_AS(nodep->pinsp(), Arg)->exprp(); | |
| 6088 | ✗ | classp->baseMostClassp()->needRNG(true); | |
| 6089 | v3Global.useRandomizeMethods(true); | ||
| 6090 | AstCExpr* const newp | ||
| 6091 | ✗ | = new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1, true}; | |
| 6092 | newp->addExprsp(exprp->unlinkFrBack()); | ||
| 6093 | ✗ | newp->addExprsp(new AstText{nodep->fileline(), ")", true}); | |
| 6094 | ✗ | newp->dtypeSetString(); | |
| 6095 | ✗ | nodep->replaceWith(newp); | |
| 6096 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 6097 | ✗ | return; | |
| 6098 | } else { | ||
| 6099 | ✗ | UASSERT_OBJ(false, nodep, "Bad case"); | |
| 6100 | } | ||
| 6101 | } | ||
| 6102 | ✗ | UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked"); | |
| 6103 | ✗ | if (nodep->didWidth()) return; | |
| 6104 | ✗ | if ((nodep->taskp()->classMethod() && !nodep->taskp()->isStatic()) | |
| 6105 | ✗ | && !VN_IS(m_procedurep, InitialAutomatic) | |
| 6106 | ✗ | && (!m_ftaskp || !m_ftaskp->classMethod() || m_ftaskp->isStatic()) && !m_constraintp) { | |
| 6107 | ✗ | nodep->v3error("Cannot call non-static member function " | |
| 6108 | << nodep->prettyNameQ() << " without object (IEEE 1800-2023 8.10)"); | ||
| 6109 | } | ||
| 6110 | // And do the arguments to the task/function too | ||
| 6111 | ✗ | processFTaskRefArgs(nodep); | |
| 6112 | nodep->addPinsp(withp); | ||
| 6113 | nodep->didWidth(true); | ||
| 6114 | // See steps that follow in visit(AstFuncRef*) | ||
| 6115 | } | ||
| 6116 | 41188 | void visit(AstNodeProcedure* nodep) override { | |
| 6117 | 41188 | assertAtStatement(nodep); | |
| 6118 |
1/2✓ Branch 0 taken 41188 times.
✗ Branch 1 not taken.
|
41188 | VL_RESTORER(m_procedurep); |
| 6119 | 41188 | m_procedurep = nodep; | |
| 6120 | userIterateChildren(nodep, nullptr); | ||
| 6121 | 41188 | } | |
| 6122 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4900 times.
|
4900 | void visit(AstSenItem* nodep) override { |
| 6123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4900 times.
|
4900 | UASSERT_OBJ(nodep->isClocked(), nodep, "Invalid edge"); |
| 6124 | // Optimize concat/replicate senitems; this has to be done here at the latest, otherwise we | ||
| 6125 | // emit WIDTHCONCAT if there are unsized constants | ||
| 6126 | if (VN_IS(nodep->sensp(), Concat) || VN_IS(nodep->sensp(), Replicate)) { | ||
| 6127 | auto* const concatOrReplp = VN_CAST(nodep->sensp(), NodeBiop); | ||
| 6128 | auto* const rhsp = concatOrReplp->rhsp()->unlinkFrBack(); | ||
| 6129 | ✗ | if (nodep->edgeType() == VEdgeType::ET_CHANGED) { | |
| 6130 | // If it's ET_CHANGED, split concatenations into multiple senitems | ||
| 6131 | auto* const lhsp = concatOrReplp->lhsp()->unlinkFrBack(); | ||
| 6132 | ✗ | nodep->addNextHere(new AstSenItem{lhsp->fileline(), nodep->edgeType(), lhsp}); | |
| 6133 | } // Else only use the RHS | ||
| 6134 | ✗ | nodep->replaceWith(new AstSenItem{rhsp->fileline(), nodep->edgeType(), rhsp}); | |
| 6135 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 6136 | } else { | ||
| 6137 |
1/2✓ Branch 1 taken 4900 times.
✗ Branch 2 not taken.
|
9800 | userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); |
| 6138 |
1/2✓ Branch 0 taken 4900 times.
✗ Branch 1 not taken.
|
4900 | if (nodep->edgeType().anEdge()) { |
| 6139 | AstNodeDType* const sensDtp = nodep->sensp()->dtypep()->skipRefp(); | ||
| 6140 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4900 times.
|
4900 | if (sensDtp->isDouble()) { |
| 6141 | ✗ | nodep->sensp()->v3error( | |
| 6142 | "Edge event control not legal on real type (IEEE 1800-2023 6.12.1)"); | ||
| 6143 |
1/2✓ Branch 1 taken 4900 times.
✗ Branch 2 not taken.
|
4900 | } else if (sensDtp->basicp() && !sensDtp->basicp()->keyword().isIntNumeric()) { |
| 6144 | ✗ | nodep->sensp()->v3error("Edge event control not legal on non-integral type " | |
| 6145 | "(IEEE 1800-2023 9.4.2)"); | ||
| 6146 | } | ||
| 6147 | } | ||
| 6148 | } | ||
| 6149 | 4900 | } | |
| 6150 | ✗ | void visit(AstClockingItem* nodep) override { | |
| 6151 | ✗ | nodep->exprp()->foreach([nodep](AstVarRef* const refp) { | |
| 6152 | ✗ | refp->access(nodep->direction().isWritable() ? VAccess::WRITE : VAccess::READ); | |
| 6153 | }); | ||
| 6154 | ✗ | userIterateChildren(nodep, WidthVP{SELF, PRELIM}.p()); | |
| 6155 | } | ||
| 6156 | ✗ | void visit(AstWait* nodep) override { | |
| 6157 | ✗ | if (VN_IS(m_ftaskp, Func)) { | |
| 6158 | ✗ | nodep->v3error("Wait statements are not legal in functions. Suggest use a task " | |
| 6159 | "(IEEE 1800-2023 13.4.4)"); | ||
| 6160 | ✗ | VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); | |
| 6161 | ✗ | return; | |
| 6162 | } | ||
| 6163 | ✗ | if (nodep->fileline()->timingOn()) { | |
| 6164 | ✗ | if (v3Global.opt.timing().isSetTrue()) { | |
| 6165 | ✗ | iterateCheckBool(nodep, "Wait", nodep->condp(), | |
| 6166 | BOTH); // it's like an if() condition. | ||
| 6167 | // TODO check also inside complex event expressions | ||
| 6168 | if (AstNodeVarRef* const varrefp = VN_CAST(nodep->condp(), NodeVarRef)) { | ||
| 6169 | ✗ | if (varrefp->isEvent()) { | |
| 6170 | ✗ | varrefp->v3error("Wait statement conditions do not take raw events" | |
| 6171 | " (IEEE 1800-2023 15.5.3)\n" | ||
| 6172 | << varrefp->warnMore() << "... Suggest use '" | ||
| 6173 | << varrefp->prettyName() << ".triggered'"); | ||
| 6174 | } | ||
| 6175 | } | ||
| 6176 | iterateNull(nodep->stmtsp()); | ||
| 6177 | ✗ | return; | |
| 6178 | ✗ | } else if (v3Global.opt.timing().isSetFalse()) { | |
| 6179 | ✗ | nodep->v3warn(E_NOTIMING, "Wait statements require --timing"); | |
| 6180 | } else { | ||
| 6181 | ✗ | nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how wait " | |
| 6182 | "statements should be handled"); | ||
| 6183 | } | ||
| 6184 | } | ||
| 6185 | // If we ignore timing: | ||
| 6186 | // Statements we'll just execute immediately; equivalent to if they followed this | ||
| 6187 | ✗ | if (AstNode* const stmtsp = nodep->stmtsp()) { | |
| 6188 | ✗ | stmtsp->unlinkFrBackWithNext(); | |
| 6189 | ✗ | nodep->replaceWith(stmtsp); | |
| 6190 | } else { | ||
| 6191 | nodep->unlinkFrBack(); | ||
| 6192 | } | ||
| 6193 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 6194 | } | ||
| 6195 | ✗ | void visit(AstWith* nodep) override { | |
| 6196 | // Should otherwise be underneath a method call | ||
| 6197 | ✗ | AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp(); | |
| 6198 | { | ||
| 6199 | ✗ | VL_RESTORER(m_withp); | |
| 6200 | ✗ | m_withp = nodep; | |
| 6201 | userIterateChildren(nodep->indexArgRefp(), nullptr); | ||
| 6202 | userIterateChildren(nodep->valueArgRefp(), nullptr); | ||
| 6203 | ✗ | if (!nodep->exprp()->hasDType()) { | |
| 6204 | ✗ | userIterateAndNext(nodep->exprp(), nullptr); | |
| 6205 | } else { | ||
| 6206 | ✗ | if (vdtypep) { | |
| 6207 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{nodep->dtypep(), PRELIM}.p()); | |
| 6208 | } else { // 'sort with' allows arbitrary type | ||
| 6209 | ✗ | userIterateAndNext(nodep->exprp(), WidthVP{SELF, PRELIM}.p()); | |
| 6210 | } | ||
| 6211 | } | ||
| 6212 | |||
| 6213 | ✗ | if (!nodep->exprp()->hasDType()) { | |
| 6214 | ✗ | nodep->dtypeSetVoid(); | |
| 6215 | } else { | ||
| 6216 | nodep->dtypeFrom(nodep->exprp()); | ||
| 6217 | ✗ | iterateCheckAssign(nodep, "'with' return value", nodep->exprp(), FINAL, | |
| 6218 | nodep->dtypep()); | ||
| 6219 | } | ||
| 6220 | } | ||
| 6221 | } | ||
| 6222 | ✗ | void visit(AstLambdaArgRef* nodep) override { | |
| 6223 | ✗ | UASSERT_OBJ(m_withp, nodep, "LambdaArgRef not underneath 'with' lambda"); | |
| 6224 | ✗ | if (nodep->index()) { | |
| 6225 | nodep->dtypeFrom(m_withp->indexArgRefp()); | ||
| 6226 | } else { | ||
| 6227 | nodep->dtypeFrom(m_withp->valueArgRefp()); | ||
| 6228 | } | ||
| 6229 | } | ||
| 6230 | 485 | void visit(AstNetlist* nodep) override { | |
| 6231 | // Iterate modules backwards, in bottom-up order. That's faster | ||
| 6232 | userIterateChildrenBackwardsConst(nodep, nullptr); | ||
| 6233 | 485 | } | |
| 6234 | |||
| 6235 | //-------------------- | ||
| 6236 | // Default | ||
| 6237 | ✗ | void visit(AstNodeExpr* nodep) override { | |
| 6238 | ✗ | if (!nodep->didWidth()) { | |
| 6239 | ✗ | nodep->v3fatalSrc( | |
| 6240 | "Visit function missing? Widthed function missing for math node: " << nodep); | ||
| 6241 | } | ||
| 6242 | userIterateChildren(nodep, nullptr); | ||
| 6243 | } | ||
| 6244 | 32238 | void visit(AstNode* nodep) override { | |
| 6245 | // Default: Just iterate | ||
| 6246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32238 times.
|
32238 | UASSERT_OBJ(!m_vup, nodep, |
| 6247 | "Visit function missing? Widthed expectation for this node: " << nodep); | ||
| 6248 | userIterateChildren(nodep, nullptr); | ||
| 6249 | 32238 | } | |
| 6250 | |||
| 6251 | //---------------------------------------------------------------------- | ||
| 6252 | // WIDTH METHODs -- all iterate | ||
| 6253 | |||
| 6254 | ✗ | void visit_Or_Lu64(AstNodeUniop* nodep) { | |
| 6255 | // CALLER: AstBitsToRealD | ||
| 6256 | // Real: Output real | ||
| 6257 | // LHS presumed self-determined, then coerced to real | ||
| 6258 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 6259 | ✗ | nodep->dtypeSetDouble(); | |
| 6260 | ✗ | AstNodeDType* const subDTypep = nodep->findLogicDType(64, 64, VSigning::UNSIGNED); | |
| 6261 | // Self-determined operand | ||
| 6262 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); | |
| 6263 | ✗ | iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP); | |
| 6264 | } | ||
| 6265 | } | ||
| 6266 | ✗ | void visit(AstIToRD* nodep) override { | |
| 6267 | // Real: Output real | ||
| 6268 | // LHS presumed self-determined, then coerced to real | ||
| 6269 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 6270 | ✗ | nodep->dtypeSetDouble(); | |
| 6271 | // Self-determined operand (TODO check if numeric type) | ||
| 6272 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); | |
| 6273 | if (nodep->lhsp()->isSigned()) { | ||
| 6274 | ✗ | nodep->replaceWith( | |
| 6275 | ✗ | new AstISToRD{nodep->fileline(), nodep->lhsp()->unlinkFrBack()}); | |
| 6276 | ✗ | VL_DO_DANGLING(nodep->deleteTree(), nodep); | |
| 6277 | } | ||
| 6278 | } | ||
| 6279 | } | ||
| 6280 | ✗ | void visit(AstISToRD* nodep) override { | |
| 6281 | // Real: Output real | ||
| 6282 | // LHS presumed self-determined, then coerced to real | ||
| 6283 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 6284 | ✗ | nodep->dtypeSetDouble(); | |
| 6285 | // Self-determined operand (TODO check if numeric type) | ||
| 6286 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); | |
| 6287 | } | ||
| 6288 | } | ||
| 6289 | ✗ | void visit_Os32_Lr(AstNodeUniop* nodep) { | |
| 6290 | // CALLER: RToI | ||
| 6291 | // Real: LHS real | ||
| 6292 | // LHS presumed self-determined, then coerced to real | ||
| 6293 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 6294 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6295 | ✗ | nodep->dtypeSetSigned32(); | |
| 6296 | } | ||
| 6297 | } | ||
| 6298 | ✗ | void visit_Ou64_Lr(AstNodeUniop* nodep) { | |
| 6299 | // CALLER: RealToBits | ||
| 6300 | // Real: LHS real | ||
| 6301 | // LHS presumed self-determined, then coerced to real | ||
| 6302 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 6303 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6304 | ✗ | nodep->dtypeSetUInt64(); | |
| 6305 | } | ||
| 6306 | } | ||
| 6307 | |||
| 6308 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 76776 times.
|
76776 | void visit_log_not(AstNode* nodep) { |
| 6309 | // CALLER: LogNot | ||
| 6310 | // Width-check: lhs 1 bit | ||
| 6311 | // Real: Allowed; implicitly compares with zero | ||
| 6312 | // We calculate the width of the UNDER expression. | ||
| 6313 | // We then check its width to see if it's legal, and edit if not | ||
| 6314 | // We finally set the width of our output | ||
| 6315 | // IEEE-2012: Table 11-21 and 11.8.1 (same as RedAnd): | ||
| 6316 | // LHS is self-determined | ||
| 6317 | // Width: 1 bit out | ||
| 6318 | // Sign: unsigned out (11.8.1) | ||
| 6319 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 76776 times.
|
76776 | UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!"); |
| 6320 |
2/2✓ Branch 0 taken 41317 times.
✓ Branch 1 taken 35459 times.
|
76776 | if (m_vup->prelim()) { |
| 6321 | 41317 | iterateCheckBool(nodep, "LHS", nodep->op1p(), BOTH); | |
| 6322 | 41317 | nodep->dtypeSetBit(); | |
| 6323 | } | ||
| 6324 | 76776 | } | |
| 6325 | 16862 | void visit_log_and_or(AstNodeBiop* nodep) { | |
| 6326 | // CALLER: LogAnd, LogOr, LogEq, LogIf | ||
| 6327 | // Widths: 1 bit out, lhs 1 bit, rhs 1 bit | ||
| 6328 | // IEEE-2012 Table 11-21: | ||
| 6329 | // LHS is self-determined | ||
| 6330 | // RHS is self-determined | ||
| 6331 |
2/2✓ Branch 0 taken 9090 times.
✓ Branch 1 taken 7772 times.
|
16862 | if (m_vup->prelim()) { |
| 6332 | 9090 | iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6333 | 9090 | iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 6334 | 9090 | nodep->dtypeSetBit(); | |
| 6335 | } | ||
| 6336 | 16862 | } | |
| 6337 | 79374 | void visit_red_and_or(AstNodeUniop* nodep) { | |
| 6338 | // CALLER: RedAnd, RedOr, ... | ||
| 6339 | // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE) | ||
| 6340 | // IEEE-2012: Table 11-21 and 11.8.1: | ||
| 6341 | // LHS is self-determined | ||
| 6342 | // Width: 1 bit out | ||
| 6343 | // Sign: unsigned out (11.8.1) | ||
| 6344 |
2/2✓ Branch 0 taken 58091 times.
✓ Branch 1 taken 21283 times.
|
79374 | if (m_vup->prelim()) { |
| 6345 | 58091 | iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); | |
| 6346 | 58091 | nodep->dtypeSetBit(); | |
| 6347 | } | ||
| 6348 | 79374 | } | |
| 6349 | ✗ | void visit_red_unknown(AstNodeUniop* nodep) { | |
| 6350 | // CALLER: IsUnknown | ||
| 6351 | // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE) | ||
| 6352 | // IEEE-2012: Table 11-21 and 11.8.1: | ||
| 6353 | // LHS is self-determined | ||
| 6354 | // Width: 1 bit out | ||
| 6355 | // Sign: unsigned out (11.8.1) | ||
| 6356 | ✗ | if (m_vup->prelim()) { | |
| 6357 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 6358 | ✗ | nodep->dtypeSetBit(); | |
| 6359 | } | ||
| 6360 | } | ||
| 6361 | |||
| 6362 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 51594 times.
|
51594 | void visit_cmp_eq_gt(AstNodeBiop* nodep, bool realok) { |
| 6363 | // CALLER: AstEq, AstGt, ..., AstLtS | ||
| 6364 | // Real allowed if and only if real_lhs set | ||
| 6365 | // See IEEE-2012 11.4.4, and 11.8.1: | ||
| 6366 | // Widths: 1 bit out, width is max of LHS or RHS | ||
| 6367 | // Sign: signed compare (not output) if both signed, compare is signed, | ||
| 6368 | // width mismatches sign extend | ||
| 6369 | // else, compare is unsigned, **zero-extends** | ||
| 6370 | // Real: If either real, other side becomes real and real compare | ||
| 6371 | // TODO: chandle/class handle/iface handle: WildEq/WildNeq same as Eq/Neq | ||
| 6372 | // TODO: chandle/class handle/iface handle only allowed to self-compare or against null | ||
| 6373 | // TODO: chandle/class handle/iface handle no relational compares | ||
| 6374 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 51594 times.
|
51594 | UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); |
| 6375 |
2/2✓ Branch 0 taken 28263 times.
✓ Branch 1 taken 23331 times.
|
51594 | if (m_vup->prelim()) { |
| 6376 | 28263 | userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 6377 | 28263 | userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 6378 |
2/4✓ Branch 1 taken 28263 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 28263 times.
|
28263 | if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { |
| 6379 | ✗ | if (!realok) { | |
| 6380 | ✗ | nodep->v3error("Real is illegal operand to ?== operator"); | |
| 6381 | ✗ | AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; | |
| 6382 | ✗ | nodep->replaceWith(newp); | |
| 6383 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 6384 | ✗ | return; | |
| 6385 | } | ||
| 6386 | ✗ | if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) { | |
| 6387 | VL_DANGLING(nodep); | ||
| 6388 | nodep = newp; // Process new node instead | ||
| 6389 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL); | |
| 6390 | ✗ | iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL); | |
| 6391 | } | ||
| 6392 |
2/4✓ Branch 1 taken 28263 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28263 times.
✗ Branch 5 not taken.
|
28263 | } else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) { |
| 6393 | ✗ | if (AstNodeBiop* const newp = replaceWithNVersion(nodep)) { | |
| 6394 | VL_DANGLING(nodep); | ||
| 6395 | nodep = newp; // Process new node instead | ||
| 6396 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), FINAL); | |
| 6397 | ✗ | iterateCheckString(nodep, "RHS", nodep->rhsp(), FINAL); | |
| 6398 | } | ||
| 6399 | } else { | ||
| 6400 | const bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned(); | ||
| 6401 |
2/2✓ Branch 1 taken 1348 times.
✓ Branch 2 taken 26915 times.
|
28263 | if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, signedFl)) { |
| 6402 | VL_DANGLING(nodep); | ||
| 6403 | nodep = newp; // Process new node instead | ||
| 6404 | } | ||
| 6405 |
4/6✓ Branch 0 taken 28263 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11360 times.
✓ Branch 3 taken 16903 times.
✓ Branch 4 taken 28263 times.
✗ Branch 5 not taken.
|
67886 | const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width()); |
| 6406 |
3/4✓ Branch 0 taken 28263 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11360 times.
✓ Branch 3 taken 16903 times.
|
56526 | const int ewidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); |
| 6407 | AstNodeDType* const subDTypep | ||
| 6408 |
2/2✓ Branch 0 taken 25338 times.
✓ Branch 1 taken 2925 times.
|
53601 | = nodep->findLogicDType(width, ewidth, VSigning::fromBool(signedFl)); |
| 6409 | bool warnOn = true; | ||
| 6410 |
2/2✓ Branch 0 taken 58 times.
✓ Branch 1 taken 28205 times.
|
28263 | if (!signedFl && width == 32) { |
| 6411 | // Waive on unsigned < or <= if RHS is narrower, since can't give wrong answer | ||
| 6412 | if ((VN_IS(nodep, Lt) || VN_IS(nodep, Lte)) | ||
| 6413 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 8 times.
|
18 | && (nodep->lhsp()->width() >= nodep->rhsp()->widthMin())) { |
| 6414 | warnOn = false; | ||
| 6415 | } | ||
| 6416 | // Waive on unsigned > or >= if RHS is wider, since can't give wrong answer | ||
| 6417 | if ((VN_IS(nodep, Gt) || VN_IS(nodep, Gte)) | ||
| 6418 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 11 times.
|
18 | && (nodep->lhsp()->widthMin() >= nodep->rhsp()->width())) { |
| 6419 | warnOn = false; | ||
| 6420 | } | ||
| 6421 | } | ||
| 6422 |
2/2✓ Branch 0 taken 25338 times.
✓ Branch 1 taken 2925 times.
|
53601 | iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, |
| 6423 | (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn); | ||
| 6424 | 28263 | iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, | |
| 6425 | (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn); | ||
| 6426 | } | ||
| 6427 | 28263 | nodep->dtypeSetBit(); | |
| 6428 | } | ||
| 6429 | } | ||
| 6430 | ✗ | void visit_cmp_real(AstNodeBiop* nodep) { | |
| 6431 | // CALLER: EqD, LtD | ||
| 6432 | // Widths: 1 bit out, lhs width == rhs width | ||
| 6433 | // Signed compare (not output) if both sides signed | ||
| 6434 | // Real if and only if real_allow set | ||
| 6435 | // IEEE, 11.4.4: relational compares (<,>,<=,>=,==,===,!=,!==) use | ||
| 6436 | // "zero padding" on unsigned | ||
| 6437 | ✗ | UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); | |
| 6438 | ✗ | if (m_vup->prelim()) { | |
| 6439 | // See similar handling in visit_cmp_eq_gt where created | ||
| 6440 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6441 | ✗ | iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 6442 | ✗ | nodep->dtypeSetBit(); | |
| 6443 | } | ||
| 6444 | } | ||
| 6445 | ✗ | void visit_cmp_string(AstNodeBiop* nodep) { | |
| 6446 | // CALLER: EqN, LtN | ||
| 6447 | // Widths: 1 bit out, lhs width == rhs width | ||
| 6448 | // String compare (not output) | ||
| 6449 | // Real if and only if real_allow set | ||
| 6450 | ✗ | UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); | |
| 6451 | ✗ | if (m_vup->prelim()) { | |
| 6452 | // See similar handling in visit_cmp_eq_gt where created | ||
| 6453 | ✗ | iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6454 | ✗ | iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 6455 | ✗ | nodep->dtypeSetBit(); | |
| 6456 | } | ||
| 6457 | } | ||
| 6458 | ✗ | void visit_cmp_type(AstNodeBiop* nodep) { | |
| 6459 | // CALLER: EqT, LtT | ||
| 6460 | // Widths: 1 bit out | ||
| 6461 | // Data type compare (not output) | ||
| 6462 | ✗ | if (m_vup->prelim()) { | |
| 6463 | ✗ | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); | |
| 6464 | ✗ | userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p()); | |
| 6465 | ✗ | const AstAttrOf* const lhsap = VN_AS(nodep->lhsp(), AttrOf); | |
| 6466 | ✗ | const AstAttrOf* const rhsap = VN_AS(nodep->rhsp(), AttrOf); | |
| 6467 | ✗ | UASSERT_OBJ(lhsap->attrType() == VAttrType::TYPEID, lhsap, | |
| 6468 | "Type compare expects type reference"); | ||
| 6469 | ✗ | UASSERT_OBJ(rhsap->attrType() == VAttrType::TYPEID, rhsap, | |
| 6470 | "Type compare expects type reference"); | ||
| 6471 | ✗ | AstNodeDType* const lhsDtp = VN_AS(lhsap->fromp(), NodeDType); | |
| 6472 | ✗ | AstNodeDType* const rhsDtp = VN_AS(rhsap->fromp(), NodeDType); | |
| 6473 | ✗ | UINFO(9, "==type lhsDtp " << lhsDtp << endl); | |
| 6474 | ✗ | UINFO(9, "==type rhsDtp " << lhsDtp << endl); | |
| 6475 | const bool invert = VN_IS(nodep, NeqT); | ||
| 6476 | const bool identical | ||
| 6477 | ✗ | = AstNode::computeCastable(lhsDtp, rhsDtp, nodep) == VCastable::SAMEISH; | |
| 6478 | ✗ | UINFO(9, "== " << identical << endl); | |
| 6479 | ✗ | const bool eq = invert ^ identical; | |
| 6480 | ✗ | AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitTrue{}, eq}; | |
| 6481 | ✗ | nodep->replaceWith(newp); | |
| 6482 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 6483 | } | ||
| 6484 | } | ||
| 6485 | |||
| 6486 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 47767 times.
|
47767 | void visit_negate_not(AstNodeUniop* nodep, bool real_ok) { |
| 6487 | // CALLER: (real_ok=false) Not | ||
| 6488 | // CALLER: (real_ok=true) Negate - allow real numbers | ||
| 6489 | // Signed: From lhs | ||
| 6490 | // IEEE-2012 Table 11-21: | ||
| 6491 | // Widths: out width = lhs width | ||
| 6492 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 47767 times.
|
47767 | UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!"); |
| 6493 |
2/2✓ Branch 0 taken 25750 times.
✓ Branch 1 taken 22017 times.
|
47767 | if (m_vup->prelim()) { |
| 6494 | 25750 | userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 6495 |
2/2✓ Branch 0 taken 17409 times.
✓ Branch 1 taken 8341 times.
|
25750 | if (!real_ok) checkCvtUS(nodep->lhsp()); |
| 6496 | } | ||
| 6497 |
3/4✓ Branch 0 taken 7167 times.
✓ Branch 1 taken 32259 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15508 times.
|
47767 | if (real_ok && nodep->lhsp()->isDouble()) { |
| 6498 | ✗ | spliceCvtD(nodep->lhsp()); | |
| 6499 | ✗ | if (AstNodeUniop* const newp = replaceWithDVersion(nodep)) { | |
| 6500 | VL_DANGLING(nodep); | ||
| 6501 | nodep = newp; // Process new node instead | ||
| 6502 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6503 | ✗ | nodep->dtypeSetDouble(); | |
| 6504 | ✗ | return; | |
| 6505 | } | ||
| 6506 | } else { | ||
| 6507 | // Note there aren't yet uniops that need version changes | ||
| 6508 | // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned()) | ||
| 6509 | } | ||
| 6510 |
2/2✓ Branch 0 taken 25750 times.
✓ Branch 1 taken 22017 times.
|
47767 | if (m_vup->prelim()) nodep->dtypeFrom(nodep->lhsp()); |
| 6511 |
2/2✓ Branch 0 taken 25750 times.
✓ Branch 1 taken 22017 times.
|
47767 | if (m_vup->final()) { |
| 6512 | 25750 | AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); | |
| 6513 | nodep->dtypep(expDTypep); // Propagate expression type to negation | ||
| 6514 | AstNodeDType* const subDTypep = expDTypep; | ||
| 6515 | // Some warning suppressions | ||
| 6516 | bool lhsWarn = true; | ||
| 6517 | if (VN_IS(nodep, Negate)) { | ||
| 6518 | // Warn if user wants extra bit from carry | ||
| 6519 |
2/2✓ Branch 0 taken 8196 times.
✓ Branch 1 taken 145 times.
|
8341 | if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false; |
| 6520 | } | ||
| 6521 | 25750 | iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP, | |
| 6522 | lhsWarn); | ||
| 6523 | } | ||
| 6524 | } | ||
| 6525 | |||
| 6526 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 296760 times.
|
296760 | void visit_signed_unsigned(AstNodeUniop* nodep, VSigning rs_out) { |
| 6527 | // CALLER: Signed, Unsigned | ||
| 6528 | // Width: lhs is self determined width | ||
| 6529 | // See IEEE-2012 6.24.1: | ||
| 6530 | // Width: Returns packed array, of size $bits(expression). | ||
| 6531 | // Sign: Output sign is as specified by operation | ||
| 6532 | // TODO: Type: Two-state if input is two-state, else four-state | ||
| 6533 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 296760 times.
|
296760 | UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!"); |
| 6534 |
2/2✓ Branch 0 taken 159340 times.
✓ Branch 1 taken 137420 times.
|
296760 | if (m_vup->prelim()) { |
| 6535 | 159340 | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); | |
| 6536 | 159340 | checkCvtUS(nodep->lhsp()); | |
| 6537 | const int width = nodep->lhsp()->width(); | ||
| 6538 | 159340 | AstNodeDType* const expDTypep = nodep->findLogicDType(width, width, rs_out); | |
| 6539 | nodep->dtypep(expDTypep); | ||
| 6540 | AstNodeDType* const subDTypep = expDTypep; | ||
| 6541 | // The child's width is self determined | ||
| 6542 | 159340 | iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP); | |
| 6543 | } | ||
| 6544 | 296760 | } | |
| 6545 | |||
| 6546 | 33739 | void visit_shift(AstNodeBiop* nodep) { | |
| 6547 | // CALLER: ShiftL, ShiftR, ShiftRS | ||
| 6548 | // Widths: Output width from lhs, rhs<33 bits | ||
| 6549 | // Signed: Output signed iff LHS signed; unary operator | ||
| 6550 | // See IEEE 2012 11.4.10: | ||
| 6551 | // RHS is self-determined. RHS is always treated as unsigned, has no effect on result. | ||
| 6552 | 33739 | iterate_shift_prelim(nodep); | |
| 6553 |
1/2✓ Branch 0 taken 33739 times.
✗ Branch 1 not taken.
|
67478 | nodep->dtypeChgSigned(nodep->lhsp()->isSigned()); |
| 6554 | 33739 | const AstNodeBiop* const newp = iterate_shift_final(nodep); | |
| 6555 | VL_DANGLING(nodep); | ||
| 6556 | (void)newp; // Ununused | ||
| 6557 | 33739 | } | |
| 6558 | 33739 | void iterate_shift_prelim(AstNodeBiop* nodep) { | |
| 6559 | // Shifts | ||
| 6560 | // See IEEE-2012 11.4.10 and Table 11-21. | ||
| 6561 | // RHS is self-determined. RHS is always treated as unsigned, has no effect on result. | ||
| 6562 |
2/2✓ Branch 0 taken 18174 times.
✓ Branch 1 taken 15565 times.
|
33739 | if (m_vup->prelim()) { |
| 6563 | 18174 | userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p()); | |
| 6564 | 18174 | checkCvtUS(nodep->lhsp()); | |
| 6565 | 18174 | iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); | |
| 6566 | nodep->dtypeFrom(nodep->lhsp()); | ||
| 6567 | } | ||
| 6568 | 33739 | } | |
| 6569 | 33739 | AstNodeBiop* iterate_shift_final(AstNodeBiop* nodep) { | |
| 6570 | // Nodep maybe edited | ||
| 6571 |
2/2✓ Branch 0 taken 18174 times.
✓ Branch 1 taken 15565 times.
|
33739 | if (m_vup->final()) { |
| 6572 | 18174 | AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); | |
| 6573 | AstNodeDType* const subDTypep = expDTypep; | ||
| 6574 | nodep->dtypep(expDTypep); | ||
| 6575 | // ShiftRS converts to ShiftR, but not vice-versa | ||
| 6576 | if (VN_IS(nodep, ShiftRS)) { | ||
| 6577 |
2/2✓ Branch 1 taken 799 times.
✓ Branch 2 taken 3835 times.
|
4634 | if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, nodep->isSigned())) { |
| 6578 | VL_DANGLING(nodep); | ||
| 6579 | nodep = newp; // Process new node instead | ||
| 6580 | } | ||
| 6581 | } | ||
| 6582 | bool warnOn = true; | ||
| 6583 | // No warning if "X = 1'b1<<N"; assume user is doing what they want | ||
| 6584 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 18174 times.
|
18174 | if (nodep->lhsp()->isOne() && VN_IS(nodep->backp(), NodeAssign)) warnOn = false; |
| 6585 | // We don't currently suppress these, as it's the upper operator (e.g. assign) | ||
| 6586 | // that reports the WIDTHEXPAND. | ||
| 6587 | AstConst* const shiftp = VN_CAST(nodep->rhsp(), Const); | ||
| 6588 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 1939 times.
✓ Branch 3 taken 1939 times.
✗ Branch 4 not taken.
|
3878 | if (shiftp && !shiftp->num().isFourState() && shiftp->width() <= 32) { |
| 6589 | 1939 | const int64_t shiftVal = shiftp->num().toSQuad(); | |
| 6590 | if (VN_IS(nodep, ShiftL)) { | ||
| 6591 |
1/6✓ Branch 0 taken 939 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
939 | if (shiftVal > 0 && nodep->width() == nodep->lhsp()->width() + shiftVal) |
| 6592 | warnOn = false; | ||
| 6593 | } | ||
| 6594 | } | ||
| 6595 | 18174 | iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP, | |
| 6596 | warnOn); | ||
| 6597 |
2/2✓ Branch 0 taken 129 times.
✓ Branch 1 taken 18045 times.
|
18174 | if (nodep->rhsp()->width() > 32) { |
| 6598 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 129 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
129 | if (shiftp && shiftp->num().mostSetBitP1() <= 32) { |
| 6599 | // If (number)<<96'h1, then make it into (number)<<32'h1 | ||
| 6600 | ✗ | V3Number num(shiftp, 32, 0); | |
| 6601 | ✗ | num.opAssign(shiftp->num()); | |
| 6602 | AstNode* const shiftrhsp = nodep->rhsp(); | ||
| 6603 | ✗ | nodep->rhsp()->replaceWith(new AstConst{shiftrhsp->fileline(), num}); | |
| 6604 | ✗ | VL_DO_DANGLING(shiftrhsp->deleteTree(), shiftrhsp); | |
| 6605 | } | ||
| 6606 | } | ||
| 6607 | } | ||
| 6608 | 33739 | return nodep; // May edit | |
| 6609 | } | ||
| 6610 | |||
| 6611 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43397 times.
|
43397 | void visit_boolexpr_and_or(AstNodeBiop* nodep) { |
| 6612 | // CALLER: And, Or, Xor, ... | ||
| 6613 | // Lint widths: out width = lhs width = rhs width | ||
| 6614 | // Signed: if lhs & rhs signed | ||
| 6615 | // IEEE-2012 Table 11-21: | ||
| 6616 | // Width: max(LHS, RHS) | ||
| 6617 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43397 times.
|
43397 | UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!"); |
| 6618 | // If errors are off, we need to follow the spec; thus we really need to do the max() | ||
| 6619 | // because the rhs could be larger, and we need to have proper editing to get the widths | ||
| 6620 | // to be the same for our operations. | ||
| 6621 |
2/2✓ Branch 0 taken 22644 times.
✓ Branch 1 taken 20753 times.
|
43397 | if (m_vup->prelim()) { // First stage evaluation |
| 6622 | // Determine expression widths only relying on what's in the subops | ||
| 6623 | 22644 | userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 6624 | 22644 | userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 6625 | 22644 | checkCvtUS(nodep->lhsp()); | |
| 6626 | 22644 | checkCvtUS(nodep->rhsp()); | |
| 6627 |
4/6✓ Branch 0 taken 22644 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9379 times.
✓ Branch 3 taken 13265 times.
✓ Branch 4 taken 22644 times.
✗ Branch 5 not taken.
|
54667 | const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width()); |
| 6628 |
4/6✓ Branch 0 taken 22644 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9379 times.
✓ Branch 3 taken 13265 times.
✓ Branch 4 taken 22644 times.
✗ Branch 5 not taken.
|
54667 | const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); |
| 6629 | const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); | ||
| 6630 | 22644 | nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned)); | |
| 6631 | } | ||
| 6632 |
2/2✓ Branch 0 taken 22644 times.
✓ Branch 1 taken 20753 times.
|
43397 | if (m_vup->final()) { |
| 6633 | 22644 | AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); | |
| 6634 | AstNodeDType* const subDTypep = expDTypep; | ||
| 6635 | nodep->dtypep(expDTypep); | ||
| 6636 | // Error report and change sizes for suboperands of this node. | ||
| 6637 | 22644 | iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); | |
| 6638 | 22644 | iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); | |
| 6639 | } | ||
| 6640 | 43397 | } | |
| 6641 | |||
| 6642 | 25182 | void visit_add_sub_replace(AstNodeBiop* nodep, bool real_ok) { | |
| 6643 | // CALLER: (real_ok=false) AddS, SubS, ... | ||
| 6644 | // CALLER: (real_ok=true) Add, Sub, ... | ||
| 6645 | // Widths: out width = lhs width = rhs width | ||
| 6646 | // Signed: Replace operator with signed operator, or signed to unsigned | ||
| 6647 | // Real: Replace operator with real operator | ||
| 6648 | // IEEE-2012 Table 11-21: | ||
| 6649 | // Width: max(LHS, RHS) | ||
| 6650 | // If errors are off, we need to follow the spec; thus we really need to do the max() | ||
| 6651 | // because the rhs could be larger, and we need to have proper editing to get the widths | ||
| 6652 | // to be the same for our operations. | ||
| 6653 | // | ||
| 6654 | // if (debug() >= 9) { UINFO(0,"rus "<<m_vup<<endl); nodep->dumpTree("- rusin: "); } | ||
| 6655 |
2/2✓ Branch 0 taken 13574 times.
✓ Branch 1 taken 11608 times.
|
25182 | if (m_vup->prelim()) { // First stage evaluation |
| 6656 | // Determine expression widths only relying on what's in the subops | ||
| 6657 | 13574 | userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 6658 | 13574 | userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); | |
| 6659 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13573 times.
|
13574 | if (!real_ok) { |
| 6660 | 1 | checkCvtUS(nodep->lhsp()); | |
| 6661 | 1 | checkCvtUS(nodep->rhsp()); | |
| 6662 | } | ||
| 6663 |
2/4✓ Branch 1 taken 13574 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 13574 times.
|
13574 | if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { |
| 6664 | ✗ | spliceCvtD(nodep->lhsp()); | |
| 6665 | ✗ | spliceCvtD(nodep->rhsp()); | |
| 6666 | ✗ | if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) { | |
| 6667 | VL_DANGLING(nodep); | ||
| 6668 | nodep = newp; // Process new node instead | ||
| 6669 | } | ||
| 6670 | ✗ | nodep->dtypeSetDouble(); | |
| 6671 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL); | |
| 6672 | ✗ | iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL); | |
| 6673 | ✗ | return; | |
| 6674 | } else { | ||
| 6675 |
4/6✓ Branch 0 taken 13574 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5544 times.
✓ Branch 3 taken 8030 times.
✓ Branch 4 taken 13574 times.
✗ Branch 5 not taken.
|
32692 | const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width()); |
| 6676 |
4/6✓ Branch 0 taken 13574 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5544 times.
✓ Branch 3 taken 8030 times.
✓ Branch 4 taken 13574 times.
✗ Branch 5 not taken.
|
32692 | const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); |
| 6677 | const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); | ||
| 6678 | 13574 | nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned)); | |
| 6679 | } | ||
| 6680 | } | ||
| 6681 |
2/2✓ Branch 0 taken 13574 times.
✓ Branch 1 taken 11608 times.
|
25182 | if (m_vup->final()) { |
| 6682 | // Parent's data type was computed using the max(upper, nodep->dtype) | ||
| 6683 | 13574 | AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); | |
| 6684 | AstNodeDType* const subDTypep = expDTypep; | ||
| 6685 | nodep->dtypep(expDTypep); | ||
| 6686 | // We don't use LHS && RHS -- unspecified language corner, see t_math_signed5 test | ||
| 6687 | // bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); | ||
| 6688 |
2/2✓ Branch 1 taken 239 times.
✓ Branch 2 taken 13335 times.
|
13574 | if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, expDTypep->isSigned())) { |
| 6689 | VL_DANGLING(nodep); | ||
| 6690 | nodep = newp; // Process new node instead | ||
| 6691 | } | ||
| 6692 | // Some warning suppressions | ||
| 6693 | bool lhsWarn = true; | ||
| 6694 | bool rhsWarn = true; | ||
| 6695 | if (VN_IS(nodep, Add) || VN_IS(nodep, Sub)) { | ||
| 6696 | // Warn if user wants extra bit from carry | ||
| 6697 |
2/2✓ Branch 0 taken 351 times.
✓ Branch 1 taken 8699 times.
|
9050 | if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false; |
| 6698 |
2/2✓ Branch 0 taken 335 times.
✓ Branch 1 taken 8715 times.
|
9050 | if (subDTypep->widthMin() == (nodep->rhsp()->widthMin() + 1)) rhsWarn = false; |
| 6699 | } else if (VN_IS(nodep, Mul) || VN_IS(nodep, MulS)) { | ||
| 6700 |
1/2✓ Branch 0 taken 4524 times.
✗ Branch 1 not taken.
|
4524 | if (subDTypep->widthMin() >= (nodep->lhsp()->widthMin())) lhsWarn = false; |
| 6701 |
1/2✓ Branch 0 taken 4524 times.
✗ Branch 1 not taken.
|
4524 | if (subDTypep->widthMin() >= (nodep->rhsp()->widthMin())) rhsWarn = false; |
| 6702 | } | ||
| 6703 | // Final call, so make sure children check their sizes | ||
| 6704 | // Error report and change sizes for suboperands of this node. | ||
| 6705 | 13574 | iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP, | |
| 6706 | lhsWarn); | ||
| 6707 | 13574 | iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP, | |
| 6708 | rhsWarn); | ||
| 6709 | } | ||
| 6710 | // if (debug() >= 9) nodep->dumpTree("- rusou: "); | ||
| 6711 | } | ||
| 6712 | ✗ | void visit_real_add_sub(AstNodeBiop* nodep) { | |
| 6713 | // CALLER: AddD, MulD, ... | ||
| 6714 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 6715 | // Note similar steps in visit_add_sub_replace promotion to double | ||
| 6716 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6717 | ✗ | iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH); | |
| 6718 | ✗ | nodep->dtypeSetDouble(); | |
| 6719 | } | ||
| 6720 | } | ||
| 6721 | ✗ | void visit_real_neg_ceil(AstNodeUniop* nodep) { | |
| 6722 | // CALLER: Negate, Ceil, Log, ... | ||
| 6723 | ✗ | if (m_vup->prelim()) { // First stage evaluation | |
| 6724 | // See alsl visit_negate_not conversion | ||
| 6725 | ✗ | iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); | |
| 6726 | ✗ | nodep->dtypeSetDouble(); | |
| 6727 | } | ||
| 6728 | } | ||
| 6729 | |||
| 6730 | //---------------------------------------------------------------------- | ||
| 6731 | // LOWER LEVEL WIDTH METHODS (none iterate) | ||
| 6732 | |||
| 6733 |
2/2✓ Branch 0 taken 243608 times.
✓ Branch 1 taken 568322 times.
|
811930 | bool widthBad(AstNode* nodep, AstNodeDType* expDTypep) { |
| 6734 | const int expWidth = expDTypep->width(); | ||
| 6735 | int expWidthMin = expDTypep->widthMin(); | ||
| 6736 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 811930 times.
|
811930 | UASSERT_OBJ(nodep->dtypep(), nodep, |
| 6737 | "Under node " << nodep->prettyTypeName() | ||
| 6738 | << " has no dtype?? Missing Visitor func?"); | ||
| 6739 |
2/4✓ Branch 1 taken 811930 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 811930 times.
|
811930 | if (expDTypep->basicp()->untyped() || nodep->dtypep()->basicp()->untyped()) return false; |
| 6740 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 811930 times.
|
811930 | UASSERT_OBJ(nodep->width() != 0, nodep, |
| 6741 | "Under node " << nodep->prettyTypeName() | ||
| 6742 | << " has no expected width?? Missing Visitor func?"); | ||
| 6743 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 811930 times.
|
811930 | UASSERT_OBJ(expWidth != 0, nodep, |
| 6744 | "Node " << nodep->prettyTypeName() | ||
| 6745 | << " has no expected width?? Missing Visitor func?"); | ||
| 6746 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 811930 times.
|
811930 | if (expWidthMin == 0) expWidthMin = expWidth; |
| 6747 |
2/2✓ Branch 0 taken 302244 times.
✓ Branch 1 taken 509686 times.
|
811930 | if (nodep->dtypep()->width() == expWidth) return false; |
| 6748 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 509686 times.
|
509686 | if (nodep->dtypep()->widthSized() && nodep->width() != expWidthMin) return true; |
| 6749 | ✗ | if (!nodep->dtypep()->widthSized() && nodep->widthMin() > expWidthMin) return true; | |
| 6750 | return false; | ||
| 6751 | } | ||
| 6752 | |||
| 6753 | 332570 | void fixWidthExtend(AstNodeExpr* nodep, AstNodeDType* expDTypep, ExtendRule extendRule) { | |
| 6754 | // Fix the width mismatch by extending or truncating bits | ||
| 6755 | // *ONLY* call this from checkWidth() | ||
| 6756 | // Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312; | ||
| 6757 | // A(CONSTwide)+B becomes A(CONSTwidened)+B | ||
| 6758 | // A(somewide)+B becomes A(TRUNC(somewide,width))+B | ||
| 6759 | // or A(EXTRACT(somewide,width,0))+B | ||
| 6760 | // Sign extension depends on the type of the *present* | ||
| 6761 | // node, while the output dtype is the *expected* sign. | ||
| 6762 | // It is reasonable to have sign extension with unsigned output, | ||
| 6763 | // for example $unsigned(a)+$signed(b), the SIGNED(B) will be unsigned dtype out | ||
| 6764 |
1/8✗ Branch 1 not taken.
✓ Branch 2 taken 332570 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
332570 | UINFO(4, |
| 6765 | " widthExtend_(r=" << static_cast<int>(extendRule) << ") old: " << nodep << endl); | ||
| 6766 |
1/2✓ Branch 0 taken 332570 times.
✗ Branch 1 not taken.
|
332570 | if (extendRule == EXTEND_OFF) return; |
| 6767 | AstConst* const constp = VN_CAST(nodep, Const); | ||
| 6768 | const int expWidth = expDTypep->width(); | ||
| 6769 | if (constp && !constp->num().isNegative()) { | ||
| 6770 | // Save later constant propagation work, just right-size it. | ||
| 6771 |
1/2✓ Branch 1 taken 59550 times.
✗ Branch 2 not taken.
|
59550 | V3Number num(nodep, expWidth); |
| 6772 |
1/2✓ Branch 1 taken 59550 times.
✗ Branch 2 not taken.
|
59550 | num.opAssign(constp->num()); |
| 6773 | num.isSigned(false); | ||
| 6774 |
2/4✓ Branch 1 taken 59550 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 59550 times.
✗ Branch 5 not taken.
|
59550 | AstNodeExpr* const newp = new AstConst{nodep->fileline(), num}; |
| 6775 |
1/2✓ Branch 1 taken 59550 times.
✗ Branch 2 not taken.
|
59550 | constp->replaceWith(newp); |
| 6776 | VL_DO_DANGLING(pushDeletep(constp), constp); | ||
| 6777 | VL_DANGLING(nodep); | ||
| 6778 | nodep = newp; | ||
| 6779 |
2/2✓ Branch 0 taken 32371 times.
✓ Branch 1 taken 240649 times.
|
273020 | } else if (expWidth < nodep->width()) { |
| 6780 | // Trunc - Extract | ||
| 6781 |
1/2✓ Branch 1 taken 32371 times.
✗ Branch 2 not taken.
|
32371 | VNRelinker linker; |
| 6782 | nodep->unlinkFrBack(&linker); | ||
| 6783 |
2/4✓ Branch 1 taken 32371 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32371 times.
✗ Branch 5 not taken.
|
32371 | AstNodeExpr* const newp = new AstSel{nodep->fileline(), nodep, 0, expWidth}; |
| 6784 | newp->didWidth(true); // Don't replace dtype with unsigned | ||
| 6785 | linker.relink(newp); | ||
| 6786 | nodep = newp; | ||
| 6787 | } else { | ||
| 6788 | // Extend | ||
| 6789 |
1/2✓ Branch 1 taken 240649 times.
✗ Branch 2 not taken.
|
240649 | VNRelinker linker; |
| 6790 | nodep->unlinkFrBack(&linker); | ||
| 6791 | bool doSigned = false; | ||
| 6792 |
3/4✓ Branch 0 taken 174071 times.
✓ Branch 1 taken 48142 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18436 times.
|
240649 | switch (extendRule) { |
| 6793 | case EXTEND_ZERO: doSigned = false; break; | ||
| 6794 |
3/4✓ Branch 0 taken 174071 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6720 times.
✓ Branch 3 taken 22780 times.
|
203571 | case EXTEND_EXP: doSigned = nodep->isSigned() && expDTypep->isSigned(); break; |
| 6795 |
1/2✓ Branch 0 taken 48142 times.
✗ Branch 1 not taken.
|
48142 | case EXTEND_LHS: doSigned = nodep->isSigned(); break; |
| 6796 | ✗ | default: nodep->v3fatalSrc("bad case"); | |
| 6797 | } | ||
| 6798 | AstNodeExpr* const newp | ||
| 6799 |
1/6✓ Branch 1 taken 18202 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
18202 | = (doSigned ? static_cast<AstNodeExpr*>(new AstExtendS{nodep->fileline(), nodep}) |
| 6800 |
1/2✓ Branch 1 taken 222447 times.
✗ Branch 2 not taken.
|
222447 | : static_cast<AstNodeExpr*>(new AstExtend{nodep->fileline(), nodep})); |
| 6801 | newp->didWidth(true); | ||
| 6802 | linker.relink(newp); | ||
| 6803 | nodep = newp; | ||
| 6804 | } | ||
| 6805 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 332570 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
332570 | if (expDTypep->isDouble() && !nodep->isDouble()) { |
| 6806 | // For AstVar init() among others | ||
| 6807 | // TODO do all to-real and to-integer conversions in this function | ||
| 6808 | // rather than in callers | ||
| 6809 | ✗ | AstNodeExpr* const newp = spliceCvtD(nodep); | |
| 6810 | nodep = newp; | ||
| 6811 | } | ||
| 6812 | nodep->dtypep(expDTypep); | ||
| 6813 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 332570 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
332570 | UINFO(4, " _new: " << nodep << endl); |
| 6814 | } | ||
| 6815 | |||
| 6816 | 101512 | void fixWidthReduce(AstNodeExpr* nodep) { | |
| 6817 | // Fix the width mismatch by adding a reduction OR operator | ||
| 6818 | // IF (A(CONSTwide)) becomes IF (A(CONSTreduced)) | ||
| 6819 | // IF (A(somewide)) becomes IF (A(REDOR(somewide))) | ||
| 6820 | // Attempt to fix it quietly | ||
| 6821 | const int expWidth = 1; | ||
| 6822 | const int expSigned = false; | ||
| 6823 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 101512 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
101512 | UINFO(4, " widthReduce_old: " << nodep << endl); |
| 6824 | AstConst* const constp = VN_CAST(nodep, Const); | ||
| 6825 | if (constp) { | ||
| 6826 |
1/2✓ Branch 1 taken 13258 times.
✗ Branch 2 not taken.
|
13258 | V3Number num(nodep, expWidth); |
| 6827 |
1/2✓ Branch 1 taken 13258 times.
✗ Branch 2 not taken.
|
13258 | num.opRedOr(constp->num()); |
| 6828 | num.isSigned(expSigned); | ||
| 6829 |
2/4✓ Branch 1 taken 13258 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13258 times.
✗ Branch 5 not taken.
|
13258 | AstNodeExpr* const newp = new AstConst{nodep->fileline(), num}; |
| 6830 |
1/2✓ Branch 1 taken 13258 times.
✗ Branch 2 not taken.
|
13258 | constp->replaceWith(newp); |
| 6831 |
1/2✓ Branch 1 taken 13258 times.
✗ Branch 2 not taken.
|
13258 | VL_DO_DANGLING(constp->deleteTree(), constp); |
| 6832 | VL_DANGLING(nodep); | ||
| 6833 | nodep = newp; | ||
| 6834 | } else { | ||
| 6835 |
1/2✓ Branch 1 taken 88254 times.
✗ Branch 2 not taken.
|
88254 | VNRelinker linker; |
| 6836 | nodep->unlinkFrBack(&linker); | ||
| 6837 |
2/4✓ Branch 1 taken 88254 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 88254 times.
✗ Branch 5 not taken.
|
88254 | AstNodeExpr* const newp = new AstRedOr{nodep->fileline(), nodep}; |
| 6838 | linker.relink(newp); | ||
| 6839 | nodep = newp; | ||
| 6840 | } | ||
| 6841 | 101512 | nodep->dtypeChgWidthSigned(expWidth, expWidth, VSigning::fromBool(expSigned)); | |
| 6842 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 101512 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
101512 | UINFO(4, " _new: " << nodep << endl); |
| 6843 | 101512 | } | |
| 6844 | |||
| 6845 | 334646 | bool fixAutoExtend(AstNodeExpr*& nodepr, int expWidth) { | |
| 6846 | // For SystemVerilog '0,'1,'x,'z, autoextend and don't warn | ||
| 6847 |
1/2✓ Branch 0 taken 334646 times.
✗ Branch 1 not taken.
|
334646 | if (AstConst* const constp = VN_CAST(nodepr, Const)) { |
| 6848 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 127235 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
127235 | if (constp->num().autoExtend() && !constp->num().sized() && constp->width() == 1) { |
| 6849 | // Make it the proper size. Careful of proper extension of 0's/1's | ||
| 6850 | ✗ | V3Number num(constp, expWidth); | |
| 6851 | ✗ | num.opRepl(constp->num(), expWidth); // {width{'1}} | |
| 6852 | ✗ | AstNodeExpr* const newp = new AstConst{constp->fileline(), num}; | |
| 6853 | // Spec says always unsigned with proper width | ||
| 6854 | ✗ | if (debug() > 4) constp->dumpTree("- fixAutoExtend_old: "); | |
| 6855 | ✗ | if (debug() > 4) newp->dumpTree("- _new: "); | |
| 6856 | ✗ | constp->replaceWith(newp); | |
| 6857 | ✗ | VL_DO_DANGLING(constp->deleteTree(), constp); | |
| 6858 | // Tell caller the new constp, and that we changed it. | ||
| 6859 | ✗ | nodepr = newp; | |
| 6860 | return true; | ||
| 6861 | } | ||
| 6862 | // X/Z also upper bit extend. In pre-SV only to 32-bits, SV forever. | ||
| 6863 | else if (!constp->num().sized() | ||
| 6864 | // Make it the proper size. Careful of proper extension of 0's/1's | ||
| 6865 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 127235 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
127235 | && expWidth > 32 && constp->num().isMsbXZ()) { |
| 6866 | ✗ | constp->v3warn(WIDTHXZEXPAND, "Unsized constant being X/Z extended to " | |
| 6867 | << expWidth | ||
| 6868 | << " bits: " << constp->prettyName()); | ||
| 6869 | ✗ | V3Number num(constp, expWidth); | |
| 6870 | ✗ | num.opExtendXZ(constp->num(), constp->width()); | |
| 6871 | ✗ | AstNodeExpr* const newp = new AstConst{constp->fileline(), num}; | |
| 6872 | // Spec says always unsigned with proper width | ||
| 6873 | ✗ | if (debug() > 4) constp->dumpTree("- fixUnszExtend_old: "); | |
| 6874 | ✗ | if (debug() > 4) newp->dumpTree("- _new: "); | |
| 6875 | ✗ | constp->replaceWith(newp); | |
| 6876 | ✗ | VL_DO_DANGLING(constp->deleteTree(), constp); | |
| 6877 | // Tell caller the new constp, and that we changed it. | ||
| 6878 | ✗ | nodepr = newp; | |
| 6879 | return true; | ||
| 6880 | } | ||
| 6881 | } | ||
| 6882 | return false; // No change | ||
| 6883 | } | ||
| 6884 | ✗ | bool isBaseClassRecurse(const AstClass* const cls1p, const AstClass* const cls2p) { | |
| 6885 | // Returns true if cls1p and cls2p are equal or if cls1p is a base class of cls2p | ||
| 6886 | ✗ | if (cls1p == cls2p) return true; | |
| 6887 | ✗ | for (const AstClassExtends* cextp = cls2p->extendsp(); cextp; | |
| 6888 | cextp = VN_CAST(cextp->nextp(), ClassExtends)) { | ||
| 6889 | ✗ | if (isBaseClassRecurse(cls1p, cextp->classp())) return true; | |
| 6890 | } | ||
| 6891 | return false; | ||
| 6892 | } | ||
| 6893 | 136888 | void checkClassAssign(AstNode* nodep, const char* side, AstNode* rhsp, | |
| 6894 | AstNodeDType* const lhsDTypep) { | ||
| 6895 | if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep->skipRefp(), ClassRefDType)) { | ||
| 6896 | ✗ | UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type"); | |
| 6897 | AstNodeDType* const rhsDtypep = rhsp->dtypep()->skipRefp(); | ||
| 6898 | if (AstClassRefDType* const rhsClassRefp = VN_CAST(rhsDtypep, ClassRefDType)) { | ||
| 6899 | ✗ | if (isBaseClassRecurse(lhsClassRefp->classp(), rhsClassRefp->classp())) return; | |
| 6900 | } else if (rhsp->isNull()) { | ||
| 6901 | return; | ||
| 6902 | } | ||
| 6903 | ✗ | nodep->v3error(side << " expects a " << lhsClassRefp->prettyTypeName() << ", got " | |
| 6904 | << rhsDtypep->prettyTypeName()); | ||
| 6905 | } | ||
| 6906 | } | ||
| 6907 | 13476 | static bool similarDTypeRecurse(const AstNodeDType* const node1p, | |
| 6908 | const AstNodeDType* const node2p) { | ||
| 6909 | 13476 | return node1p->skipRefp()->similarDType(node2p->skipRefp()); | |
| 6910 | } | ||
| 6911 | ✗ | void iterateCheckFileDesc(AstNode* nodep, AstNode* underp, Stage stage) { | |
| 6912 | ✗ | UASSERT_OBJ(stage == BOTH, nodep, "Bad call"); | |
| 6913 | // underp may change as a result of replacement | ||
| 6914 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p()); | |
| 6915 | AstNodeDType* const expDTypep = underp->findUInt32DType(); | ||
| 6916 | underp | ||
| 6917 | ✗ | = iterateCheck(nodep, "file_descriptor", underp, SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 6918 | (void)underp; // cppcheck | ||
| 6919 | } | ||
| 6920 | ✗ | void iterateCheckSigned32(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { | |
| 6921 | // Coerce child to signed32 if not already. Child is self-determined | ||
| 6922 | // underp may change as a result of replacement | ||
| 6923 | ✗ | if (stage & PRELIM) { | |
| 6924 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p()); | |
| 6925 | } | ||
| 6926 | ✗ | if (stage & FINAL) { | |
| 6927 | AstNodeDType* const expDTypep = nodep->findSigned32DType(); | ||
| 6928 | ✗ | underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 6929 | } | ||
| 6930 | (void)underp; // cppcheck | ||
| 6931 | } | ||
| 6932 | ✗ | void iterateCheckDelay(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { | |
| 6933 | // Coerce child to 64-bit delay if not already. Child is self-determined | ||
| 6934 | // underp may change as a result of replacement | ||
| 6935 | ✗ | if (stage & PRELIM) { | |
| 6936 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p()); | |
| 6937 | } | ||
| 6938 | ✗ | if (stage & FINAL) { | |
| 6939 | AstNodeDType* expDTypep; | ||
| 6940 | ✗ | if (underp->dtypep()->skipRefp()->isDouble()) { // V3Timing will later convert double | |
| 6941 | expDTypep = nodep->findDoubleDType(); | ||
| 6942 | } else { | ||
| 6943 | ✗ | FileLine* const newFl = new FileLine{underp->fileline()}; | |
| 6944 | ✗ | newFl->warnOff(V3ErrorCode::WIDTHEXPAND, true); | |
| 6945 | underp->fileline(newFl); | ||
| 6946 | ✗ | expDTypep = nodep->findLogicDType(64, 64, VSigning::UNSIGNED); | |
| 6947 | } | ||
| 6948 | ✗ | underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP, false); | |
| 6949 | } | ||
| 6950 | (void)underp; // cppcheck | ||
| 6951 | } | ||
| 6952 | ✗ | void iterateCheckReal(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { | |
| 6953 | // Coerce child to real if not already. Child is self-determined | ||
| 6954 | // e.g. nodep=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST) | ||
| 6955 | // Don't need separate PRELIM and FINAL(double) calls; | ||
| 6956 | // as if resolves to double, the BOTH correctly resolved double, | ||
| 6957 | // otherwise self-determined was correct | ||
| 6958 | // underp may change as a result of replacement | ||
| 6959 | ✗ | if (stage & PRELIM) { | |
| 6960 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p()); | |
| 6961 | } | ||
| 6962 | ✗ | if (stage & FINAL) { | |
| 6963 | AstNodeDType* const expDTypep = nodep->findDoubleDType(); | ||
| 6964 | ✗ | underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 6965 | } | ||
| 6966 | (void)underp; // cppcheck | ||
| 6967 | } | ||
| 6968 | ✗ | void iterateCheckString(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { | |
| 6969 | AstNodeDType* const expDTypep = nodep->findStringDType(); | ||
| 6970 | ✗ | if (stage & PRELIM) { | |
| 6971 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, PRELIM}.p()); | |
| 6972 | } | ||
| 6973 | ✗ | if (stage & FINAL) { | |
| 6974 | ✗ | underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 6975 | } | ||
| 6976 | (void)underp; // cppcheck | ||
| 6977 | } | ||
| 6978 | ✗ | void iterateCheckTyped(AstNode* nodep, const char* side, AstNode* underp, | |
| 6979 | AstNodeDType* expDTypep, Stage stage) { | ||
| 6980 | ✗ | if (stage & PRELIM) { | |
| 6981 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, PRELIM}.p()); | |
| 6982 | } | ||
| 6983 | ✗ | if (stage & FINAL) { | |
| 6984 | ✗ | underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 6985 | } | ||
| 6986 | (void)underp; // cppcheck | ||
| 6987 | } | ||
| 6988 | 641281 | void iterateCheckSizedSelf(AstNode* nodep, const char* side, AstNode* underp, Determ determ, | |
| 6989 | Stage stage) { | ||
| 6990 | // Coerce child to any sized-number data type; child is self-determined | ||
| 6991 | // i.e. isolated from expected type. | ||
| 6992 | // e.g. nodep=CONCAT, underp=lhs in CONCAT(lhs,rhs) | ||
| 6993 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 641281 times.
|
641281 | UASSERT_OBJ(determ == SELF, nodep, "Bad call"); |
| 6994 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 641281 times.
|
641281 | UASSERT_OBJ(stage == FINAL || stage == BOTH, nodep, "Bad call"); |
| 6995 | // underp may change as a result of replacement | ||
| 6996 |
1/2✓ Branch 0 taken 641281 times.
✗ Branch 1 not taken.
|
641281 | if (stage & PRELIM) { |
| 6997 | 1282562 | underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p()); | |
| 6998 | } | ||
| 6999 | 641281 | underp = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr)) : underp; | |
| 7000 | AstNodeDType* const expDTypep = underp->dtypep(); | ||
| 7001 | 641281 | underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); | |
| 7002 | (void)underp; // cppcheck | ||
| 7003 | 641281 | } | |
| 7004 | 136888 | void iterateCheckAssign(AstNode* nodep, const char* side, AstNode* rhsp, Stage stage, | |
| 7005 | AstNodeDType* lhsDTypep) { | ||
| 7006 | // Check using assignment-like context rules | ||
| 7007 | // if (debug()) nodep->dumpTree("- checkass: "); | ||
| 7008 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 136888 times.
|
136888 | UASSERT_OBJ(stage == FINAL, nodep, "Bad width call"); |
| 7009 | // Create unpacked byte from string see IEEE 1800-2023 5.9 | ||
| 7010 | if (AstConst* constp = VN_CAST(rhsp, Const)) { | ||
| 7011 | if (const AstUnpackArrayDType* const arrayp | ||
| 7012 | = VN_CAST(lhsDTypep->skipRefp(), UnpackArrayDType)) { | ||
| 7013 | if (AstBasicDType* basicp = VN_CAST(arrayp->subDTypep()->skipRefp(), BasicDType)) { | ||
| 7014 | ✗ | if (basicp->width() == 8 && constp->num().isFromString()) { | |
| 7015 | AstInitArray* newp = new AstInitArray{ | ||
| 7016 | constp->fileline(), lhsDTypep, | ||
| 7017 | ✗ | new AstConst{constp->fileline(), AstConst::WidthedValue{}, 8, 0}}; | |
| 7018 | ✗ | for (int aindex = arrayp->lo(); aindex <= arrayp->hi(); ++aindex) { | |
| 7019 | ✗ | int cindex = arrayp->declRange().ascending() ? (arrayp->hi() - aindex) | |
| 7020 | : (aindex - arrayp->lo()); | ||
| 7021 | ✗ | V3Number selected{constp, 8}; | |
| 7022 | ✗ | selected.opSel(constp->num(), cindex * 8 + 7, cindex * 8); | |
| 7023 | ✗ | UINFO(0, " aindex=" << aindex << " cindex=" << cindex | |
| 7024 | << " c=" << selected << endl); | ||
| 7025 | ✗ | if (!selected.isFourState()) { | |
| 7026 | ✗ | if (const uint32_t c = selected.toUInt()) { | |
| 7027 | ✗ | newp->addIndexValuep( | |
| 7028 | aindex, new AstConst{constp->fileline(), | ||
| 7029 | ✗ | AstConst::WidthedValue{}, 8, c}); | |
| 7030 | } | ||
| 7031 | } | ||
| 7032 | } | ||
| 7033 | ✗ | UINFO(6, " unpackFromString: " << nodep << endl); | |
| 7034 | ✗ | rhsp->replaceWith(newp); | |
| 7035 | VL_DO_DANGLING(pushDeletep(rhsp), rhsp); | ||
| 7036 | rhsp = newp; | ||
| 7037 | } | ||
| 7038 | } | ||
| 7039 | } | ||
| 7040 | } | ||
| 7041 | // We iterate and size the RHS based on the result of RHS evaluation | ||
| 7042 | 136888 | checkClassAssign(nodep, side, rhsp, lhsDTypep); | |
| 7043 | const bool lhsStream | ||
| 7044 | 123115 | = (VN_IS(nodep, NodeAssign) && VN_IS(VN_AS(nodep, NodeAssign)->lhsp(), NodeStream)); | |
| 7045 | 136888 | rhsp = iterateCheck(nodep, side, rhsp, ASSIGN, FINAL, lhsDTypep, | |
| 7046 | lhsStream ? EXTEND_OFF : EXTEND_LHS); | ||
| 7047 | // if (debug()) nodep->dumpTree("- checkout: "); | ||
| 7048 | (void)rhsp; // cppcheck | ||
| 7049 | 136888 | } | |
| 7050 | |||
| 7051 | 159866 | void iterateCheckBool(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { | |
| 7052 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 159866 times.
|
159866 | UASSERT_OBJ(stage == BOTH, nodep, |
| 7053 | "Bad call"); // Booleans always self-determined so do BOTH at once | ||
| 7054 | // Underp is used in a self-determined but boolean context, reduce a | ||
| 7055 | // multibit number to one bit | ||
| 7056 | // stage is always BOTH so not passed as argument | ||
| 7057 | // underp may change as a result of replacement | ||
| 7058 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 159866 times.
|
159866 | UASSERT_OBJ(underp, nodep, "Node has no type"); |
| 7059 | 159866 | underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, BOTH}.p()); | |
| 7060 |
2/4✓ Branch 0 taken 159866 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 159866 times.
|
159866 | UASSERT_OBJ(underp && underp->dtypep(), nodep, |
| 7061 | "Node has no type"); // Perhaps forgot to do a prelim visit on it? | ||
| 7062 | // | ||
| 7063 | // For DOUBLE under a logical op, add implied test against zero, never a warning | ||
| 7064 | AstNodeDType* const underVDTypep = underp ? underp->dtypep()->skipRefp() : nullptr; | ||
| 7065 |
1/2✓ Branch 1 taken 159866 times.
✗ Branch 2 not taken.
|
159866 | if (underp && underVDTypep->isDouble()) { |
| 7066 | ✗ | UINFO(6, " spliceCvtCmpD0: " << underp << endl); | |
| 7067 | ✗ | VNRelinker linker; | |
| 7068 | ✗ | underp->unlinkFrBack(&linker); | |
| 7069 | AstNode* const newp | ||
| 7070 | = new AstNeqD{nodep->fileline(), VN_AS(underp, NodeExpr), | ||
| 7071 | ✗ | new AstConst{nodep->fileline(), AstConst::RealDouble{}, 0.0}}; | |
| 7072 | linker.relink(newp); | ||
| 7073 | } else if (VN_IS(underVDTypep, ClassRefDType) || VN_IS(underVDTypep, IfaceRefDType) | ||
| 7074 | || (VN_IS(underVDTypep, BasicDType) | ||
| 7075 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 159866 times.
|
159866 | && VN_AS(underVDTypep, BasicDType)->keyword() == VBasicDTypeKwd::CHANDLE)) { |
| 7076 | // Allow warning-free "if (handle)" | ||
| 7077 | ✗ | VL_DO_DANGLING(fixWidthReduce(VN_AS(underp, NodeExpr)), underp); // Changed | |
| 7078 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 159866 times.
|
159866 | } else if (!underVDTypep->basicp()) { |
| 7079 | ✗ | nodep->v3error("Logical operator " << nodep->prettyTypeName() | |
| 7080 | << " expects a non-complex data type on the " | ||
| 7081 | << side << "."); | ||
| 7082 | ✗ | underp->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); | |
| 7083 | VL_DO_DANGLING(pushDeletep(underp), underp); | ||
| 7084 | } else { | ||
| 7085 | 159866 | const bool bad = widthBad(underp, nodep->findBitDType()); | |
| 7086 |
2/2✓ Branch 0 taken 101512 times.
✓ Branch 1 taken 58354 times.
|
159866 | if (bad) { |
| 7087 | { // if (warnOn), but not needed here | ||
| 7088 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 101512 times.
|
101512 | if (debug() > 4) nodep->backp()->dumpTree("- back: "); |
| 7089 |
12/32✗ Branch 0 not taken.
✓ Branch 1 taken 101512 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 9 taken 101512 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 101512 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 101512 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 101512 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 101512 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✓ Branch 26 taken 101512 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 101512 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✓ Branch 32 taken 101512 times.
✓ Branch 33 taken 7110 times.
✓ Branch 34 taken 94402 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 101512 times.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
|
819206 | nodep->v3widthWarn(1, underp->width(), |
| 7090 | "Logical operator " | ||
| 7091 | << nodep->prettyTypeName() << " expects 1 bit on the " | ||
| 7092 | << side << ", but " << side << "'s " | ||
| 7093 | << underp->prettyTypeName() << " generates " | ||
| 7094 | << underp->width() | ||
| 7095 | << (underp->width() != underp->widthMin() | ||
| 7096 | ? " or " + cvtToStr(underp->widthMin()) | ||
| 7097 | : "") | ||
| 7098 | << " bits."); | ||
| 7099 | } | ||
| 7100 | 101512 | VL_DO_DANGLING(fixWidthReduce(VN_AS(underp, NodeExpr)), underp); // Changed | |
| 7101 | } | ||
| 7102 | } | ||
| 7103 | 159866 | } | |
| 7104 | |||
| 7105 | 1292975 | AstNode* iterateCheck(AstNode* nodep, const char* side, AstNode* underp, Determ determ, | |
| 7106 | Stage stage, AstNodeDType* expDTypep, ExtendRule extendRule, | ||
| 7107 | bool warnOn = true) { | ||
| 7108 | // Perform data type check on underp, which is underneath nodep used for error reporting | ||
| 7109 | // Returns the new underp | ||
| 7110 | // Conversion to/from doubles and integers are before iterating. | ||
| 7111 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1292975 times.
|
1292975 | UASSERT_OBJ(stage == FINAL, nodep, "Bad state to iterateCheck"); |
| 7112 |
2/4✓ Branch 0 taken 1292975 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1292975 times.
|
1292975 | UASSERT_OBJ(underp && underp->dtypep(), nodep, |
| 7113 | "Node has no type"); // Perhaps forgot to do a prelim visit on it? | ||
| 7114 | if (VN_IS(underp, NodeDType)) { // Note the node itself, not node's data type | ||
| 7115 | // Must be near top of these checks as underp->dtypep() will look normal | ||
| 7116 | ✗ | underp->v3error(ucfirst(nodep->prettyOperatorName()) | |
| 7117 | << " expected non-datatype " << side << " but " | ||
| 7118 | << underp->prettyNameQ() << " is a datatype."); | ||
| 7119 |
2/2✓ Branch 0 taken 819347 times.
✓ Branch 1 taken 473628 times.
|
1292975 | } else if (expDTypep == underp->dtypep()) { // Perfect |
| 7120 | 819347 | underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p()); | |
| 7121 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 473628 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
473628 | } else if (expDTypep->isDouble() && underp->isDouble()) { // Also good |
| 7122 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p()); | |
| 7123 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 473628 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
473628 | } else if (expDTypep->isDouble() && !underp->isDouble()) { |
| 7124 | AstNode* const oldp | ||
| 7125 | = underp; // Need FINAL on children; otherwise splice would block it | ||
| 7126 | ✗ | spliceCvtD(VN_AS(underp, NodeExpr)); | |
| 7127 | ✗ | underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p()); | |
| 7128 |
2/4✓ Branch 1 taken 473628 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473628 times.
✗ Branch 5 not taken.
|
473628 | } else if (!expDTypep->isDouble() && underp->isDouble()) { |
| 7129 | AstNode* const oldp | ||
| 7130 | = underp; // Need FINAL on children; otherwise splice would block it | ||
| 7131 | ✗ | spliceCvtS(VN_AS(underp, NodeExpr), true, expDTypep->width()); // Round RHS | |
| 7132 | ✗ | underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p()); | |
| 7133 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 473628 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
473628 | } else if (expDTypep->isString() && !underp->dtypep()->isString()) { |
| 7134 | AstNode* const oldp | ||
| 7135 | = underp; // Need FINAL on children; otherwise splice would block it | ||
| 7136 | ✗ | spliceCvtString(VN_AS(underp, NodeExpr)); | |
| 7137 | ✗ | underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p()); | |
| 7138 | } else { | ||
| 7139 | 473628 | const AstBasicDType* const expBasicp = expDTypep->basicp(); | |
| 7140 | 473628 | const AstBasicDType* const underBasicp = underp->dtypep()->basicp(); | |
| 7141 |
1/2✓ Branch 0 taken 473628 times.
✗ Branch 1 not taken.
|
473628 | if (expBasicp && underBasicp) { |
| 7142 | if (const AstEnumDType* const expEnump | ||
| 7143 | = VN_CAST(expDTypep->skipRefToEnump(), EnumDType)) { | ||
| 7144 | const auto castable | ||
| 7145 | ✗ | = AstNode::computeCastable(expEnump, underp->dtypep(), underp); | |
| 7146 | ✗ | if (castable != VCastable::SAMEISH && castable != VCastable::COMPATIBLE | |
| 7147 | ✗ | && castable != VCastable::ENUM_IMPLICIT && !VN_IS(underp, Cast) | |
| 7148 | ✗ | && !VN_IS(underp, CastDynamic) && !m_enumItemp | |
| 7149 | ✗ | && !nodep->fileline()->warnIsOff(V3ErrorCode::ENUMVALUE) && warnOn) { | |
| 7150 | ✗ | underp->v3warn(ENUMVALUE, | |
| 7151 | "Implicit conversion to enum " | ||
| 7152 | << expDTypep->prettyDTypeNameQ() << " from " | ||
| 7153 | << underp->dtypep()->prettyDTypeNameQ() | ||
| 7154 | << " (IEEE 1800-2023 6.19.3)\n" | ||
| 7155 | << nodep->warnMore() | ||
| 7156 | << "... Suggest use enum's mnemonic, or static cast"); | ||
| 7157 | // if (debug()) nodep->backp()->dumpTree("- back: "); | ||
| 7158 | } | ||
| 7159 | } | ||
| 7160 | AstNodeDType* subDTypep = expDTypep; | ||
| 7161 | // We then iterate FINAL before width fixes, as if the under-operation | ||
| 7162 | // is e.g. an ADD, the ADD will auto-adjust to the proper data type | ||
| 7163 | // or if another operation e.g. ATOI will not. | ||
| 7164 |
2/2✓ Branch 0 taken 105862 times.
✓ Branch 1 taken 367766 times.
|
473628 | if (determ == SELF) { |
| 7165 | 211724 | underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, FINAL}.p()); | |
| 7166 |
2/2✓ Branch 0 taken 133885 times.
✓ Branch 1 taken 233881 times.
|
367766 | } else if (determ == ASSIGN) { |
| 7167 | // IEEE: Signedness is solely determined by the RHS | ||
| 7168 | // (underp), not by the LHS (expDTypep) | ||
| 7169 | if (underp->isSigned() != subDTypep->isSigned() | ||
| 7170 |
4/4✓ Branch 0 taken 68077 times.
✓ Branch 1 taken 65808 times.
✓ Branch 2 taken 64935 times.
✓ Branch 3 taken 3142 times.
|
201962 | || underp->width() != subDTypep->width()) { |
| 7171 |
1/2✓ Branch 0 taken 130743 times.
✗ Branch 1 not taken.
|
261486 | subDTypep = nodep->findLogicDType( |
| 7172 |
2/2✓ Branch 0 taken 32371 times.
✓ Branch 1 taken 98372 times.
|
130743 | std::max(subDTypep->width(), underp->width()), |
| 7173 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 130743 times.
✓ Branch 2 taken 32371 times.
✓ Branch 3 taken 98372 times.
|
130743 | std::max(subDTypep->widthMin(), underp->widthMin()), |
| 7174 | VSigning::fromBool(underp->isSigned())); | ||
| 7175 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 130743 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
130743 | UINFO(9, "Assignment of opposite-signed RHS to LHS: " << nodep << endl); |
| 7176 | } | ||
| 7177 | 133885 | underp = userIterateSubtreeReturnEdits(underp, WidthVP{subDTypep, FINAL}.p()); | |
| 7178 | } else { | ||
| 7179 | 233881 | underp = userIterateSubtreeReturnEdits(underp, WidthVP{subDTypep, FINAL}.p()); | |
| 7180 | } | ||
| 7181 | // Note the check uses the expected size, not the child's subDTypep as we want the | ||
| 7182 | // child node's width to end up correct for the assignment (etc) | ||
| 7183 | 473628 | widthCheckSized(nodep, side, VN_AS(underp, NodeExpr), expDTypep, extendRule, | |
| 7184 | warnOn); | ||
| 7185 | } else if (!VN_IS(nodep, Eq) && !VN_IS(nodep, Neq) | ||
| 7186 | && !VN_IS(expDTypep->skipRefp(), IfaceRefDType) | ||
| 7187 | && VN_IS(underp->dtypep()->skipRefp(), IfaceRefDType)) { | ||
| 7188 | ✗ | underp->v3error(ucfirst(nodep->prettyOperatorName()) | |
| 7189 | << " expected non-interface on " << side << " but " | ||
| 7190 | << underp->prettyNameQ() << " is an interface."); | ||
| 7191 | } else if (const AstIfaceRefDType* expIfaceRefp | ||
| 7192 | = VN_CAST(expDTypep->skipRefp(), IfaceRefDType)) { | ||
| 7193 | const AstIfaceRefDType* underIfaceRefp | ||
| 7194 | = VN_CAST(underp->dtypep()->skipRefp(), IfaceRefDType); | ||
| 7195 | if (!underIfaceRefp) { | ||
| 7196 | ✗ | underp->v3error(ucfirst(nodep->prettyOperatorName()) | |
| 7197 | << " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ() | ||
| 7198 | << " interface on " << side << " but " << underp->prettyNameQ() | ||
| 7199 | << " is not an interface."); | ||
| 7200 | ✗ | } else if (expIfaceRefp->ifaceViaCellp() != underIfaceRefp->ifaceViaCellp()) { | |
| 7201 | ✗ | underp->v3error(ucfirst(nodep->prettyOperatorName()) | |
| 7202 | << " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ() | ||
| 7203 | << " interface on " << side << " but " << underp->prettyNameQ() | ||
| 7204 | << " is a different interface (" | ||
| 7205 | << underIfaceRefp->ifaceViaCellp()->prettyNameQ() << ")."); | ||
| 7206 | } else if (underIfaceRefp->modportp() | ||
| 7207 | ✗ | && expIfaceRefp->modportp() != underIfaceRefp->modportp()) { | |
| 7208 | ✗ | underp->v3error(ucfirst(nodep->prettyOperatorName()) | |
| 7209 | << " expected " | ||
| 7210 | << (expIfaceRefp->modportp() | ||
| 7211 | ? expIfaceRefp->modportp()->prettyNameQ() | ||
| 7212 | : "no") | ||
| 7213 | << " interface modport on " << side << " but got " | ||
| 7214 | << underIfaceRefp->modportp()->prettyNameQ() << " modport."); | ||
| 7215 | } else { | ||
| 7216 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p()); | |
| 7217 | } | ||
| 7218 | } else { | ||
| 7219 | // Hope it just works out (perhaps a cast will deal with it) | ||
| 7220 | ✗ | underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p()); | |
| 7221 | } | ||
| 7222 | } | ||
| 7223 | 1292975 | return underp; | |
| 7224 | } | ||
| 7225 | |||
| 7226 | void | ||
| 7227 | 608858 | widthCheckSized(AstNode* nodep, const char* side, | |
| 7228 | AstNodeExpr* underp, // Node to be checked or have typecast added in front of | ||
| 7229 | AstNodeDType* expDTypep, ExtendRule extendRule, bool warnOn = true) { | ||
| 7230 | // Issue warnings on sized number width mismatches, then do appropriate size extension | ||
| 7231 | // Generally iterateCheck is what is wanted instead of this | ||
| 7232 | // UINFO(9,"wchk "<<side<<endl<<" "<<nodep<<endl<<" "<<underp<<endl<<" e="<<expDTypep<<" | ||
| 7233 | // i"<<warnOn<<endl); | ||
| 7234 | 608858 | const AstBasicDType* const expBasicp = expDTypep->basicp(); | |
| 7235 | 608858 | const AstBasicDType* const underBasicp = underp->dtypep()->basicp(); | |
| 7236 |
2/2✓ Branch 0 taken 516834 times.
✓ Branch 1 taken 92024 times.
|
608858 | if (expDTypep == underp->dtypep()) { |
| 7237 | return; // Same type must match | ||
| 7238 |
2/4✓ Branch 0 taken 516834 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 516834 times.
✗ Branch 3 not taken.
|
516834 | } else if (!expBasicp || expBasicp->isDouble() || !underBasicp |
| 7239 |
2/4✓ Branch 0 taken 516834 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 516834 times.
|
1033668 | || underBasicp->isDouble()) { |
| 7240 | // This is perhaps a v3fatalSrc as we should have checked the types | ||
| 7241 | // before calling widthCheck, but we may have missed a non-sized | ||
| 7242 | // check in earlier code, so might as well assume it is the users' | ||
| 7243 | // fault. | ||
| 7244 | ✗ | nodep->v3error(ucfirst(nodep->prettyOperatorName()) | |
| 7245 | << " expected non-complex non-double " << side << " in width check"); | ||
| 7246 | #if VL_DEBUG | ||
| 7247 | nodep->v3fatalSrc("widthCheckSized should not be called on doubles/complex types"); | ||
| 7248 | #endif | ||
| 7249 | ✗ | return; | |
| 7250 | } else { | ||
| 7251 | const int expWidth = expDTypep->width(); | ||
| 7252 | 516834 | int expWidthMin = expDTypep->widthMin(); | |
| 7253 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 516834 times.
|
516834 | if (expWidthMin == 0) expWidthMin = expWidth; |
| 7254 | 516834 | const bool bad = widthBad(underp, expDTypep); | |
| 7255 |
4/6✓ Branch 0 taken 182188 times.
✓ Branch 1 taken 334646 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 182188 times.
✓ Branch 5 taken 334646 times.
✗ Branch 6 not taken.
|
699022 | if ((bad || underp->width() != expWidth) && fixAutoExtend(underp /*ref*/, expWidth)) { |
| 7256 | underp = nullptr; // Changes underp | ||
| 7257 | ✗ | return; | |
| 7258 | } | ||
| 7259 | // If user has a sizing cast, assume they know what they are doing | ||
| 7260 | // (for better or worse) | ||
| 7261 | if (VN_IS(nodep->backp(), CastSize)) warnOn = false; | ||
| 7262 |
2/4✓ Branch 0 taken 516834 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 133045 times.
✗ Branch 4 not taken.
|
649879 | if (VN_IS(underp, Const) && VN_AS(underp, Const)->num().isFromString() |
| 7263 | ✗ | && expWidth > underp->width() | |
| 7264 | ✗ | && (((expWidth - underp->width()) % 8) == 0)) { // At least it's character sized | |
| 7265 | // reg [31:0] == "foo" we'll consider probably fine. | ||
| 7266 | // Maybe this should be a special warning? Not for now. | ||
| 7267 | warnOn = false; | ||
| 7268 | } | ||
| 7269 |
3/4✓ Branch 0 taken 1371 times.
✓ Branch 1 taken 4331 times.
✓ Branch 3 taken 1371 times.
✗ Branch 4 not taken.
|
5702 | if ((VN_IS(nodep, Add) && underp->width() == 1 && underp->isOne()) |
| 7270 |
3/4✓ Branch 0 taken 4366 times.
✓ Branch 1 taken 1442 times.
✓ Branch 3 taken 1442 times.
✗ Branch 4 not taken.
|
5808 | || (VN_IS(nodep, Sub) && underp->width() == 1 && underp->isOne() |
| 7271 | ✗ | && 0 == std::strcmp(side, "RHS"))) { | |
| 7272 | // "foo + 1'b1", or "foo - 1'b1" are very common, people assume | ||
| 7273 | // they extend correctly | ||
| 7274 | warnOn = false; | ||
| 7275 | } | ||
| 7276 |
2/2✓ Branch 0 taken 256282 times.
✓ Branch 1 taken 260552 times.
|
516834 | if (bad && warnOn) { |
| 7277 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 256282 times.
|
256282 | if (debug() > 4) nodep->backp()->dumpTree("- back: "); |
| 7278 | |||
| 7279 |
22/52✓ Branch 0 taken 256282 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 223911 times.
✓ Branch 3 taken 32371 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 223911 times.
✓ Branch 10 taken 256282 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 256282 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 256282 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 21 taken 256282 times.
✗ Branch 22 not taken.
✓ Branch 24 taken 256282 times.
✗ Branch 25 not taken.
✓ Branch 27 taken 256282 times.
✗ Branch 28 not taken.
✓ Branch 30 taken 256282 times.
✗ Branch 31 not taken.
✓ Branch 33 taken 256282 times.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✓ Branch 36 taken 256282 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✓ Branch 41 taken 256282 times.
✗ Branch 42 not taken.
✓ Branch 44 taken 256282 times.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✓ Branch 47 taken 256282 times.
✓ Branch 48 taken 12720 times.
✓ Branch 49 taken 243562 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 256282 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 256282 times.
✓ Branch 54 taken 81018 times.
✓ Branch 55 taken 175264 times.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
|
2831822 | nodep->v3widthWarn( |
| 7280 | expWidth, underp->width(), | ||
| 7281 | ucfirst(nodep->prettyOperatorName()) | ||
| 7282 | << " expects " << expWidth | ||
| 7283 | << (expWidth != expWidthMin ? " or " + cvtToStr(expWidthMin) : "") | ||
| 7284 | << " bits on the " << side << ", but " << side << "'s " | ||
| 7285 | << underp->prettyTypeName() << " generates " << underp->width() | ||
| 7286 | << (underp->width() != underp->widthMin() | ||
| 7287 | ? " or " + cvtToStr(underp->widthMin()) | ||
| 7288 | : "") | ||
| 7289 | << " bits."); | ||
| 7290 | } | ||
| 7291 |
4/6✓ Branch 0 taken 182188 times.
✓ Branch 1 taken 334646 times.
✓ Branch 2 taken 182188 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 182188 times.
|
699022 | if (bad || underp->width() != expWidth) { |
| 7292 | // If we're in an NodeAssign, don't truncate the RHS if the LHS is | ||
| 7293 | // a NodeStream. The streaming operator changes the rules regarding | ||
| 7294 | // which bits to truncate. | ||
| 7295 | const AstNodeAssign* assignp = VN_CAST(nodep, NodeAssign); | ||
| 7296 | const AstPin* pinp = VN_CAST(nodep, Pin); | ||
| 7297 |
2/2✓ Branch 0 taken 110292 times.
✓ Branch 1 taken 224354 times.
|
334646 | if (assignp && VN_IS(assignp->lhsp(), NodeStream)) { |
| 7298 |
4/4✓ Branch 0 taken 6926 times.
✓ Branch 1 taken 327720 times.
✓ Branch 2 taken 4850 times.
✓ Branch 3 taken 2076 times.
|
334646 | } else if (pinp && pinp->modVarp()->direction() != VDirection::INPUT) { |
| 7299 | // V3Inst::pinReconnectSimple must deal | ||
| 7300 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 2076 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
2076 | UINFO(5, "pinInSizeMismatch: " << pinp); |
| 7301 | } else { | ||
| 7302 | 332570 | VL_DO_DANGLING(fixWidthExtend(underp, expDTypep, extendRule), underp); | |
| 7303 | } | ||
| 7304 | } | ||
| 7305 | } | ||
| 7306 | } | ||
| 7307 | |||
| 7308 | //---------------------------------------------------------------------- | ||
| 7309 | // SIGNED/DOUBLE METHODS | ||
| 7310 | |||
| 7311 | 1016724 | AstNodeExpr* checkCvtUS(AstNodeExpr* nodep) { | |
| 7312 |
2/4✓ Branch 0 taken 1016724 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1016724 times.
✗ Branch 4 not taken.
|
1016724 | if (nodep && nodep->isDouble()) { |
| 7313 | ✗ | nodep->v3error("Expected integral (non-" << nodep->dtypep()->prettyDTypeNameQ() | |
| 7314 | << ") input to " | ||
| 7315 | << nodep->backp()->prettyTypeName()); | ||
| 7316 | ✗ | nodep = spliceCvtS(nodep, true, 32); | |
| 7317 | } | ||
| 7318 | 1016724 | return nodep; | |
| 7319 | } | ||
| 7320 | |||
| 7321 | ✗ | AstNodeExpr* spliceCvtD(AstNodeExpr* nodep) { | |
| 7322 | // For integer used in REAL context, convert to real | ||
| 7323 | // We don't warn here, "2.0 * 2" is common and reasonable | ||
| 7324 | ✗ | if (nodep && !nodep->dtypep()->skipRefp()->isDouble()) { | |
| 7325 | ✗ | UINFO(6, " spliceCvtD: " << nodep << endl); | |
| 7326 | ✗ | VNRelinker linker; | |
| 7327 | nodep->unlinkFrBack(&linker); | ||
| 7328 | AstNodeExpr* newp; | ||
| 7329 | ✗ | if (nodep->dtypep()->skipRefp()->isSigned()) { | |
| 7330 | ✗ | newp = new AstISToRD{nodep->fileline(), nodep}; | |
| 7331 | } else { | ||
| 7332 | ✗ | newp = new AstIToRD{nodep->fileline(), nodep}; | |
| 7333 | } | ||
| 7334 | linker.relink(newp); | ||
| 7335 | return newp; | ||
| 7336 | } else { | ||
| 7337 | ✗ | return nodep; | |
| 7338 | } | ||
| 7339 | } | ||
| 7340 | ✗ | AstNodeExpr* spliceCvtS(AstNodeExpr* nodep, bool warnOn, int width) { | |
| 7341 | // IEEE-2012 11.8.1: Signed: Type coercion creates signed | ||
| 7342 | // 11.8.2: Argument to convert is self-determined | ||
| 7343 | ✗ | if (nodep && nodep->dtypep()->skipRefp()->isDouble()) { | |
| 7344 | ✗ | UINFO(6, " spliceCvtS: " << nodep << endl); | |
| 7345 | ✗ | VNRelinker linker; | |
| 7346 | nodep->unlinkFrBack(&linker); | ||
| 7347 | if (const AstConst* const constp = VN_CAST(nodep, Const)) { | ||
| 7348 | // We convert to/from int32_t rather than use floor() as want to make sure is | ||
| 7349 | // representable in integer's number of bits | ||
| 7350 | ✗ | if (constp->isDouble() && V3Number::epsilonIntegral(constp->num().toDouble())) { | |
| 7351 | warnOn = false; | ||
| 7352 | } | ||
| 7353 | } | ||
| 7354 | ✗ | if (warnOn) nodep->v3warn(REALCVT, "Implicit conversion of real to integer"); | |
| 7355 | ✗ | AstNodeExpr* const newp = new AstRToIRoundS{nodep->fileline(), nodep}; | |
| 7356 | linker.relink(newp); | ||
| 7357 | ✗ | newp->dtypeSetBitSized(width, VSigning::SIGNED); | |
| 7358 | return newp; | ||
| 7359 | } else { | ||
| 7360 | ✗ | return nodep; | |
| 7361 | } | ||
| 7362 | } | ||
| 7363 | ✗ | AstNodeExpr* spliceCvtString(AstNodeExpr* nodep) { | |
| 7364 | // IEEE-2012 11.8.1: Signed: Type coercion creates signed | ||
| 7365 | // 11.8.2: Argument to convert is self-determined | ||
| 7366 | ✗ | if (nodep && !(nodep->dtypep()->basicp() && nodep->dtypep()->basicp()->isString())) { | |
| 7367 | ✗ | UINFO(6, " spliceCvtString: " << nodep << endl); | |
| 7368 | ✗ | VNRelinker linker; | |
| 7369 | nodep->unlinkFrBack(&linker); | ||
| 7370 | ✗ | AstNodeExpr* const newp = new AstCvtPackString{nodep->fileline(), nodep}; | |
| 7371 | linker.relink(newp); | ||
| 7372 | return newp; | ||
| 7373 | } else { | ||
| 7374 | ✗ | return nodep; | |
| 7375 | } | ||
| 7376 | } | ||
| 7377 | 46471 | AstNodeBiop* replaceWithUOrSVersion(AstNodeBiop* nodep, bool signedFlavorNeeded) { | |
| 7378 | // Given a signed/unsigned node type, create the opposite type | ||
| 7379 | // Return new node or nullptr if nothing | ||
| 7380 |
2/2✓ Branch 1 taken 39916 times.
✓ Branch 2 taken 6555 times.
|
46471 | if (signedFlavorNeeded == nodep->signedFlavor()) return nullptr; |
| 7381 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6555 times.
|
6555 | if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp()); |
| 7382 | // To simplify callers, some node types don't need to change | ||
| 7383 |
5/10✓ Branch 0 taken 343 times.
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 217 times.
✓ Branch 7 taken 259 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 5422 times.
|
6555 | switch (nodep->type()) { |
| 7384 | 343 | case VNType::atEq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7385 | 314 | case VNType::atNeq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7386 | ✗ | case VNType::atEqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7387 | ✗ | case VNType::atNeqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7388 | ✗ | case VNType::atEqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7389 | ✗ | case VNType::atNeqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7390 | 217 | case VNType::atAdd: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7391 | 259 | case VNType::atSub: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7392 | ✗ | case VNType::atShiftL: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr; | |
| 7393 | default: break; | ||
| 7394 | } | ||
| 7395 | FileLine* const fl = nodep->fileline(); | ||
| 7396 | AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); | ||
| 7397 | AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); | ||
| 7398 | AstNodeBiop* newp = nullptr; | ||
| 7399 |
6/17✓ Branch 0 taken 357 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 328 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 340 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 323 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 239 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 3835 times.
✗ Branch 16 not taken.
|
5422 | switch (nodep->type()) { |
| 7400 |
1/2✓ Branch 2 taken 357 times.
✗ Branch 3 not taken.
|
357 | case VNType::atGt: newp = new AstGtS{fl, lhsp, rhsp}; break; |
| 7401 | ✗ | case VNType::atGtS: newp = new AstGt{fl, lhsp, rhsp}; break; | |
| 7402 |
1/2✓ Branch 2 taken 328 times.
✗ Branch 3 not taken.
|
328 | case VNType::atGte: newp = new AstGteS{fl, lhsp, rhsp}; break; |
| 7403 | ✗ | case VNType::atGteS: newp = new AstGte{fl, lhsp, rhsp}; break; | |
| 7404 |
1/2✓ Branch 2 taken 340 times.
✗ Branch 3 not taken.
|
340 | case VNType::atLt: newp = new AstLtS{fl, lhsp, rhsp}; break; |
| 7405 | ✗ | case VNType::atLtS: newp = new AstLt{fl, lhsp, rhsp}; break; | |
| 7406 |
1/2✓ Branch 2 taken 323 times.
✗ Branch 3 not taken.
|
323 | case VNType::atLte: newp = new AstLteS{fl, lhsp, rhsp}; break; |
| 7407 | ✗ | case VNType::atLteS: newp = new AstLte{fl, lhsp, rhsp}; break; | |
| 7408 | ✗ | case VNType::atDiv: newp = new AstDivS{fl, lhsp, rhsp}; break; | |
| 7409 | ✗ | case VNType::atDivS: newp = new AstDiv{fl, lhsp, rhsp}; break; | |
| 7410 | ✗ | case VNType::atModDiv: newp = new AstModDivS{fl, lhsp, rhsp}; break; | |
| 7411 | ✗ | case VNType::atModDivS: newp = new AstModDiv{fl, lhsp, rhsp}; break; | |
| 7412 |
1/2✓ Branch 2 taken 239 times.
✗ Branch 3 not taken.
|
239 | case VNType::atMul: newp = new AstMulS{fl, lhsp, rhsp}; break; |
| 7413 | ✗ | case VNType::atMulS: newp = new AstMul{fl, lhsp, rhsp}; break; | |
| 7414 | ✗ | case VNType::atShiftR: newp = new AstShiftRS{fl, lhsp, rhsp}; break; | |
| 7415 | 7670 | case VNType::atShiftRS: newp = new AstShiftR{fl, lhsp, rhsp}; break; | |
| 7416 | default: // LCOV_EXCL_LINE | ||
| 7417 | ✗ | nodep->v3fatalSrc("Node needs sign change, but bad case: " << nodep); | |
| 7418 | break; | ||
| 7419 | } | ||
| 7420 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 5422 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
5422 | UINFO(6, " ReplaceWithUOrSVersion: " << nodep << " w/ " << newp << endl); |
| 7421 | 5422 | nodep->replaceWith(newp); | |
| 7422 | newp->dtypeFrom(nodep); | ||
| 7423 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 7424 | 5422 | return newp; | |
| 7425 | } | ||
| 7426 | ✗ | AstNodeBiop* replaceWithDVersion(AstNodeBiop* nodep) { | |
| 7427 | // Given a signed/unsigned node type, create the opposite type | ||
| 7428 | // Return new node or nullptr if nothing | ||
| 7429 | ✗ | if (nodep->doubleFlavor()) return nullptr; | |
| 7430 | FileLine* const fl = nodep->fileline(); | ||
| 7431 | AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); | ||
| 7432 | AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); | ||
| 7433 | AstNodeBiop* newp = nullptr; | ||
| 7434 | // No width change on output;... // All below have bool or double outputs | ||
| 7435 | ✗ | switch (nodep->type()) { | |
| 7436 | ✗ | case VNType::atAdd: newp = new AstAddD{fl, lhsp, rhsp}; break; | |
| 7437 | ✗ | case VNType::atSub: newp = new AstSubD{fl, lhsp, rhsp}; break; | |
| 7438 | ✗ | case VNType::atPow: newp = new AstPowD{fl, lhsp, rhsp}; break; | |
| 7439 | ✗ | case VNType::atEq: | |
| 7440 | ✗ | case VNType::atEqCase: newp = new AstEqD{fl, lhsp, rhsp}; break; | |
| 7441 | ✗ | case VNType::atNeq: | |
| 7442 | ✗ | case VNType::atNeqCase: newp = new AstNeqD{fl, lhsp, rhsp}; break; | |
| 7443 | ✗ | case VNType::atGt: | |
| 7444 | ✗ | case VNType::atGtS: newp = new AstGtD{fl, lhsp, rhsp}; break; | |
| 7445 | ✗ | case VNType::atGte: | |
| 7446 | ✗ | case VNType::atGteS: newp = new AstGteD{fl, lhsp, rhsp}; break; | |
| 7447 | ✗ | case VNType::atLt: | |
| 7448 | ✗ | case VNType::atLtS: newp = new AstLtD{fl, lhsp, rhsp}; break; | |
| 7449 | ✗ | case VNType::atLte: | |
| 7450 | ✗ | case VNType::atLteS: newp = new AstLteD{fl, lhsp, rhsp}; break; | |
| 7451 | ✗ | case VNType::atDiv: | |
| 7452 | ✗ | case VNType::atDivS: newp = new AstDivD{fl, lhsp, rhsp}; break; | |
| 7453 | ✗ | case VNType::atMul: | |
| 7454 | ✗ | case VNType::atMulS: newp = new AstMulD{fl, lhsp, rhsp}; break; | |
| 7455 | default: // LCOV_EXCL_LINE | ||
| 7456 | ✗ | nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep); | |
| 7457 | break; | ||
| 7458 | } | ||
| 7459 | ✗ | UINFO(6, " ReplaceWithDVersion: " << nodep << " w/ " << newp << endl); | |
| 7460 | ✗ | nodep->replaceWith(newp); | |
| 7461 | // No width change; the default created type (bool or double) is correct | ||
| 7462 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 7463 | ✗ | return newp; | |
| 7464 | } | ||
| 7465 | ✗ | AstNodeBiop* replaceWithNVersion(AstNodeBiop* nodep) { | |
| 7466 | // Given a signed/unsigned node type, replace with string version | ||
| 7467 | // Return new node or nullptr if nothing | ||
| 7468 | ✗ | if (nodep->stringFlavor()) return nullptr; | |
| 7469 | FileLine* const fl = nodep->fileline(); | ||
| 7470 | AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); | ||
| 7471 | AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); | ||
| 7472 | AstNodeBiop* newp = nullptr; | ||
| 7473 | // No width change on output;... // All below have bool or double outputs | ||
| 7474 | ✗ | switch (nodep->type()) { | |
| 7475 | ✗ | case VNType::atEq: | |
| 7476 | ✗ | case VNType::atEqCase: newp = new AstEqN{fl, lhsp, rhsp}; break; | |
| 7477 | ✗ | case VNType::atNeq: | |
| 7478 | ✗ | case VNType::atNeqCase: newp = new AstNeqN{fl, lhsp, rhsp}; break; | |
| 7479 | ✗ | case VNType::atGt: | |
| 7480 | ✗ | case VNType::atGtS: newp = new AstGtN{fl, lhsp, rhsp}; break; | |
| 7481 | ✗ | case VNType::atGte: | |
| 7482 | ✗ | case VNType::atGteS: newp = new AstGteN{fl, lhsp, rhsp}; break; | |
| 7483 | ✗ | case VNType::atLt: | |
| 7484 | ✗ | case VNType::atLtS: newp = new AstLtN{fl, lhsp, rhsp}; break; | |
| 7485 | ✗ | case VNType::atLte: | |
| 7486 | ✗ | case VNType::atLteS: newp = new AstLteN{fl, lhsp, rhsp}; break; | |
| 7487 | default: // LCOV_EXCL_LINE | ||
| 7488 | ✗ | nodep->v3fatalSrc("Node needs conversion to string, but bad case: " << nodep); | |
| 7489 | break; | ||
| 7490 | } | ||
| 7491 | ✗ | UINFO(6, " ReplaceWithNVersion: " << nodep << " w/ " << newp << endl); | |
| 7492 | ✗ | nodep->replaceWith(newp); | |
| 7493 | // No width change; the default created type (bool or string) is correct | ||
| 7494 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 7495 | ✗ | return newp; | |
| 7496 | } | ||
| 7497 | ✗ | AstNodeUniop* replaceWithDVersion(AstNodeUniop* nodep) { | |
| 7498 | // Given a signed/unsigned node type, create the opposite type | ||
| 7499 | // Return new node or nullptr if nothing | ||
| 7500 | ✗ | if (nodep->doubleFlavor()) return nullptr; | |
| 7501 | FileLine* const fl = nodep->fileline(); | ||
| 7502 | AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); | ||
| 7503 | AstNodeUniop* newp = nullptr; | ||
| 7504 | ✗ | switch (nodep->type()) { | |
| 7505 | ✗ | case VNType::atNegate: newp = new AstNegateD{fl, lhsp}; break; | |
| 7506 | default: // LCOV_EXCL_LINE | ||
| 7507 | ✗ | nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep); | |
| 7508 | break; | ||
| 7509 | } | ||
| 7510 | ✗ | UINFO(6, " ReplaceWithDVersion: " << nodep << " w/ " << newp << endl); | |
| 7511 | ✗ | nodep->replaceWith(newp); | |
| 7512 | newp->dtypeFrom(nodep); | ||
| 7513 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 7514 | ✗ | return newp; | |
| 7515 | } | ||
| 7516 | |||
| 7517 | //---------------------------------------------------------------------- | ||
| 7518 | // METHODS - strings | ||
| 7519 | |||
| 7520 | ✗ | void replaceWithSFormat(AstMethodCall* nodep, const string& format) { | |
| 7521 | // For string.itoa and similar, replace with SFormatF | ||
| 7522 | const AstArg* argp = VN_CAST(nodep->pinsp(), Arg); | ||
| 7523 | if (!argp) { | ||
| 7524 | ✗ | nodep->v3error("Argument needed for string." + nodep->prettyName() + " method"); | |
| 7525 | return; | ||
| 7526 | } | ||
| 7527 | ✗ | AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef); | |
| 7528 | AstNode* const newp = new AstAssign{ | ||
| 7529 | nodep->fileline(), fromp, | ||
| 7530 | ✗ | new AstSFormatF{nodep->fileline(), format, false, argp->exprp()->unlinkFrBack()}}; | |
| 7531 | fromp->access(VAccess::WRITE); | ||
| 7532 | pushDeletep(nodep->backp()); | ||
| 7533 | ✗ | VL_DO_DANGLING(nodep->backp()->replaceWith(newp), newp); | |
| 7534 | } | ||
| 7535 | |||
| 7536 | //---------------------------------------------------------------------- | ||
| 7537 | // METHODS - data types | ||
| 7538 | |||
| 7539 | 82406 | AstNodeDType* iterateEditMoveDTypep(AstNode* parentp, AstNodeDType* dtnodep) { | |
| 7540 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82406 times.
|
82406 | UASSERT_OBJ(dtnodep, parentp, "Caller should check for nullptr before computing dtype"); |
| 7541 | // Iterate into a data type to resolve that type. | ||
| 7542 | // The data type may either: | ||
| 7543 | // 1. Be a child (typically getChildDTypep() returns it) | ||
| 7544 | // DTypes at parse time get added as these to some node types | ||
| 7545 | // such as AstVars. | ||
| 7546 | // This function will move it to global scope (that is #2 | ||
| 7547 | // will now apply). | ||
| 7548 | // 2. Be under the Netlist and pointed to by an Ast member variable | ||
| 7549 | // (typically refDTypep() or dtypep() returns it) | ||
| 7550 | // so removing/changing a variable won't lose the dtype | ||
| 7551 | |||
| 7552 | // Case #1 above applies? | ||
| 7553 | 82406 | const bool child1 = (parentp->getChildDTypep() == dtnodep); | |
| 7554 | 82406 | const bool child2 = (parentp->getChild2DTypep() == dtnodep); | |
| 7555 |
2/2✓ Branch 0 taken 80095 times.
✓ Branch 1 taken 2311 times.
|
82406 | if (child1 || child2) { |
| 7556 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 80095 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
80095 | UINFO(9, "iterateEditMoveDTypep child iterating " << dtnodep << endl); |
| 7557 | // Iterate, this might edit the dtypes which means dtnodep now lost | ||
| 7558 | VL_DO_DANGLING(userIterate(dtnodep, nullptr), dtnodep); | ||
| 7559 | // Figure out the new dtnodep, remained a child of parent so find it there | ||
| 7560 |
1/2✓ Branch 0 taken 80095 times.
✗ Branch 1 not taken.
|
80095 | dtnodep = child1 ? parentp->getChildDTypep() : parentp->getChild2DTypep(); |
| 7561 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80095 times.
|
80095 | UASSERT_OBJ(dtnodep, parentp, "iterateEditMoveDTypep lost pointer to child"); |
| 7562 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80095 times.
|
80095 | UASSERT_OBJ(dtnodep->didWidth(), parentp, |
| 7563 | "iterateEditMoveDTypep didn't get width resolution of " | ||
| 7564 | << dtnodep->prettyTypeName()); | ||
| 7565 | // Move to under netlist | ||
| 7566 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 80095 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
80095 | UINFO(9, "iterateEditMoveDTypep child moving " << dtnodep << endl); |
| 7567 | dtnodep->unlinkFrBack(); | ||
| 7568 | v3Global.rootp()->typeTablep()->addTypesp(dtnodep); | ||
| 7569 | } | ||
| 7570 |
2/2✓ Branch 0 taken 884 times.
✓ Branch 1 taken 81522 times.
|
82406 | if (!dtnodep->didWidth()) { |
| 7571 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 884 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
884 | UINFO(9, "iterateEditMoveDTypep pointer iterating " << dtnodep << endl); |
| 7572 | // See notes in visit(AstBracketArrayDType*) | ||
| 7573 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 884 times.
|
884 | UASSERT_OBJ(!VN_IS(dtnodep, BracketArrayDType), parentp, |
| 7574 | "Brackets should have been iterated as children"); | ||
| 7575 | userIterate(dtnodep, nullptr); | ||
| 7576 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 884 times.
|
884 | UASSERT_OBJ(dtnodep->didWidth(), parentp, |
| 7577 | "iterateEditMoveDTypep didn't get width resolution"); | ||
| 7578 | } | ||
| 7579 | 82406 | return dtnodep; | |
| 7580 | } | ||
| 7581 | |||
| 7582 | ✗ | AstConst* dimensionValue(FileLine* fileline, AstNodeDType* nodep, VAttrType attrType, | |
| 7583 | int dim) { | ||
| 7584 | // Return the dimension value for the specified attribute and constant dimension | ||
| 7585 | AstNodeDType* dtypep = nodep->skipRefp(); | ||
| 7586 | VNumRange declRange; // ranged() set false | ||
| 7587 | ✗ | for (int i = 1; i <= dim; ++i) { | |
| 7588 | // UINFO(9, " dim at "<<dim<<" "<<dtypep<<endl); | ||
| 7589 | declRange = VNumRange{}; // ranged() set false | ||
| 7590 | ✗ | if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) { | |
| 7591 | ✗ | declRange = adtypep->declRange(); | |
| 7592 | ✗ | if (i < dim) dtypep = adtypep->subDTypep()->skipRefp(); | |
| 7593 | continue; | ||
| 7594 | } else if (const AstNodeUOrStructDType* const adtypep | ||
| 7595 | = VN_CAST(dtypep, NodeUOrStructDType)) { | ||
| 7596 | declRange = adtypep->declRange(); | ||
| 7597 | ✗ | break; // Sub elements don't look like arrays and can't iterate into | |
| 7598 | } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) { | ||
| 7599 | ✗ | if (adtypep->isRanged()) declRange = adtypep->declRange(); | |
| 7600 | break; | ||
| 7601 | } | ||
| 7602 | break; // LCOV_EXCL_LINE | ||
| 7603 | } | ||
| 7604 | AstConst* valp = nullptr; // If nullptr, construct from val | ||
| 7605 | int val = 0; | ||
| 7606 | ✗ | switch (attrType) { | |
| 7607 | case VAttrType::DIM_BITS: { | ||
| 7608 | int bits = 1; | ||
| 7609 | ✗ | while (dtypep) { | |
| 7610 | // UINFO(9, " bits at "<<bits<<" "<<dtypep<<endl); | ||
| 7611 | ✗ | if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) { | |
| 7612 | ✗ | bits *= adtypep->declRange().elements(); | |
| 7613 | ✗ | dtypep = adtypep->subDTypep()->skipRefp(); | |
| 7614 | ✗ | continue; | |
| 7615 | } else if (const AstNodeUOrStructDType* const adtypep | ||
| 7616 | = VN_CAST(dtypep, NodeUOrStructDType)) { | ||
| 7617 | ✗ | bits *= adtypep->width(); | |
| 7618 | ✗ | break; | |
| 7619 | } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) { | ||
| 7620 | ✗ | bits *= adtypep->width(); | |
| 7621 | ✗ | break; | |
| 7622 | } | ||
| 7623 | break; | ||
| 7624 | } | ||
| 7625 | ✗ | if (dim == 0) { | |
| 7626 | val = 0; | ||
| 7627 | ✗ | } else if (dim == 1 && !declRange.ranged() | |
| 7628 | ✗ | && bits == 1) { // $bits should be sane for non-arrays | |
| 7629 | val = nodep->width(); | ||
| 7630 | } else { | ||
| 7631 | val = bits; | ||
| 7632 | } | ||
| 7633 | break; // LCOV_EXCL_LINE | ||
| 7634 | } | ||
| 7635 | ✗ | case VAttrType::DIM_HIGH: val = !declRange.ranged() ? 0 : declRange.hi(); break; | |
| 7636 | ✗ | case VAttrType::DIM_LEFT: val = !declRange.ranged() ? 0 : declRange.left(); break; | |
| 7637 | ✗ | case VAttrType::DIM_LOW: val = !declRange.ranged() ? 0 : declRange.lo(); break; | |
| 7638 | ✗ | case VAttrType::DIM_RIGHT: val = !declRange.ranged() ? 0 : declRange.right(); break; | |
| 7639 | case VAttrType::DIM_INCREMENT: | ||
| 7640 | ✗ | val = (declRange.ranged() && declRange.ascending()) ? -1 : 1; | |
| 7641 | break; | ||
| 7642 | ✗ | case VAttrType::DIM_SIZE: val = !declRange.ranged() ? 0 : declRange.elements(); break; | |
| 7643 | ✗ | default: nodep->v3fatalSrc("Missing DIM ATTR type case"); break; | |
| 7644 | } | ||
| 7645 | ✗ | if (!valp) valp = new AstConst{fileline, AstConst::Signed32{}, val}; | |
| 7646 | ✗ | UINFO(9, " $dimension " << attrType.ascii() << "(" << cvtToHex(dtypep) << "," << dim | |
| 7647 | << ")=" << valp << endl); | ||
| 7648 | ✗ | return valp; | |
| 7649 | } | ||
| 7650 | ✗ | AstVar* dimensionVarp(AstNodeDType* nodep, VAttrType attrType, uint32_t msbdim) { | |
| 7651 | // Return a variable table which has specified dimension properties for this variable | ||
| 7652 | ✗ | const auto pair = m_tableMap.emplace(std::piecewise_construct, // | |
| 7653 | ✗ | std::forward_as_tuple(nodep, attrType), | |
| 7654 | ✗ | std::forward_as_tuple(nullptr)); | |
| 7655 | ✗ | if (pair.second) { | |
| 7656 | AstNodeArrayDType* const vardtypep | ||
| 7657 | = new AstUnpackArrayDType{nodep->fileline(), nodep->findSigned32DType(), | ||
| 7658 | ✗ | new AstRange(nodep->fileline(), msbdim, 0)}; | |
| 7659 | ✗ | AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr}; | |
| 7660 | v3Global.rootp()->typeTablep()->addTypesp(vardtypep); | ||
| 7661 | AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, | ||
| 7662 | ✗ | "__Vdimtab_" + VString::downcase(attrType.ascii()) | |
| 7663 | ✗ | + cvtToStr(m_dtTables++), | |
| 7664 | ✗ | vardtypep}; | |
| 7665 | varp->isConst(true); | ||
| 7666 | varp->isStatic(true); | ||
| 7667 | varp->valuep(initp); | ||
| 7668 | // Add to root, as don't know module we are in, and aids later structure sharing | ||
| 7669 | ✗ | v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(varp); | |
| 7670 | // Element 0 is a non-index and has speced values | ||
| 7671 | ✗ | initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, 0)); | |
| 7672 | ✗ | for (unsigned i = 1; i < msbdim + 1; ++i) { | |
| 7673 | ✗ | initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, i)); | |
| 7674 | } | ||
| 7675 | userIterate(varp, nullptr); // May have already done $unit so must do this var | ||
| 7676 | ✗ | pair.first->second = varp; | |
| 7677 | } | ||
| 7678 | ✗ | return pair.first->second; | |
| 7679 | } | ||
| 7680 | ✗ | static uint64_t enumMaxValue(const AstNode* errNodep, const AstEnumDType* adtypep) { | |
| 7681 | // Most enums unless overridden are 32 bits, so we size array | ||
| 7682 | // based on max enum value used. | ||
| 7683 | // Ideally we would have a fast algorithm when a number is | ||
| 7684 | // of small width and complete and so can use an array, and | ||
| 7685 | // a map for when the value is many bits and sparse. | ||
| 7686 | uint64_t maxval = 0; | ||
| 7687 | ✗ | for (const AstEnumItem* itemp = adtypep->itemsp(); itemp; | |
| 7688 | ✗ | itemp = VN_AS(itemp->nextp(), EnumItem)) { | |
| 7689 | const AstConst* const vconstp = VN_CAST(itemp->valuep(), Const); | ||
| 7690 | ✗ | UASSERT_OBJ(vconstp, errNodep, "Enum item without constified value"); | |
| 7691 | ✗ | if (!vconstp->num().isAnyXZ() && vconstp->toUQuad() >= maxval) | |
| 7692 | maxval = vconstp->toUQuad(); | ||
| 7693 | } | ||
| 7694 | ✗ | if (adtypep->itemsp()->width() > 64) { | |
| 7695 | ✗ | errNodep->v3warn(E_UNSUPPORTED, | |
| 7696 | "Unsupported: enum next/prev/name method on enum with > 64 bits"); | ||
| 7697 | ✗ | return 64; | |
| 7698 | } | ||
| 7699 | return maxval; | ||
| 7700 | } | ||
| 7701 | ✗ | static AstVar* enumVarp(AstEnumDType* const nodep, VAttrType attrType, bool assoc, | |
| 7702 | uint32_t msbdim) { | ||
| 7703 | // Return a variable table which has specified dimension properties for this variable | ||
| 7704 | ✗ | const auto pair = nodep->tableMap().emplace(attrType, nullptr); | |
| 7705 | ✗ | if (pair.second) { | |
| 7706 | ✗ | UINFO(9, "Construct Venumtab attr=" << attrType.ascii() << " assoc=" << assoc | |
| 7707 | << " max=" << msbdim << " for " << nodep << endl); | ||
| 7708 | AstNodeDType* basep; | ||
| 7709 | ✗ | if (attrType == VAttrType::ENUM_NAME) { | |
| 7710 | ✗ | basep = nodep->findStringDType(); | |
| 7711 | ✗ | } else if (attrType == VAttrType::ENUM_VALID) { | |
| 7712 | // TODO in theory we could bit-pack the bits in the table, but | ||
| 7713 | // would require additional operations to extract, so only | ||
| 7714 | // would be worth it for larger tables which perhaps could be | ||
| 7715 | // better handled with equation generation? | ||
| 7716 | ✗ | basep = nodep->findBitDType(); | |
| 7717 | } else { | ||
| 7718 | basep = nodep->dtypep(); | ||
| 7719 | } | ||
| 7720 | AstNodeDType* vardtypep; | ||
| 7721 | ✗ | if (assoc) { | |
| 7722 | ✗ | vardtypep = new AstAssocArrayDType{nodep->fileline(), basep, nodep}; | |
| 7723 | } else { | ||
| 7724 | vardtypep = new AstUnpackArrayDType{nodep->fileline(), basep, | ||
| 7725 | ✗ | new AstRange(nodep->fileline(), msbdim, 0)}; | |
| 7726 | } | ||
| 7727 | ✗ | AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr}; | |
| 7728 | v3Global.rootp()->typeTablep()->addTypesp(vardtypep); | ||
| 7729 | AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, | ||
| 7730 | ✗ | "__Venumtab_" + VString::downcase(attrType.ascii()) | |
| 7731 | ✗ | + cvtToStr(nodep->uniqueNum()), | |
| 7732 | ✗ | vardtypep}; | |
| 7733 | varp->lifetime(VLifetime::STATIC); | ||
| 7734 | varp->isConst(true); | ||
| 7735 | varp->isStatic(true); | ||
| 7736 | varp->valuep(initp); | ||
| 7737 | // Add to root, as don't know module we are in, and aids later structure sharing | ||
| 7738 | ✗ | v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(varp); | |
| 7739 | |||
| 7740 | // Default for all unspecified values | ||
| 7741 | ✗ | if (attrType == VAttrType::ENUM_NAME) { | |
| 7742 | ✗ | initp->defaultp(new AstConst{nodep->fileline(), AstConst::String{}, ""}); | |
| 7743 | ✗ | } else if (attrType == VAttrType::ENUM_NEXT || attrType == VAttrType::ENUM_PREV) { | |
| 7744 | ✗ | initp->defaultp( | |
| 7745 | ✗ | new AstConst{nodep->fileline(), V3Number{nodep, nodep->width(), 0}}); | |
| 7746 | ✗ | } else if (attrType == VAttrType::ENUM_VALID) { | |
| 7747 | ✗ | initp->defaultp(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); | |
| 7748 | } else { | ||
| 7749 | ✗ | nodep->v3fatalSrc("Bad case"); | |
| 7750 | } | ||
| 7751 | |||
| 7752 | // Find valid values and populate | ||
| 7753 | ✗ | UASSERT_OBJ(nodep->itemsp(), nodep, "enum without items"); | |
| 7754 | std::map<uint64_t, AstNodeExpr*> values; | ||
| 7755 | { | ||
| 7756 | AstEnumItem* const firstp = nodep->itemsp(); | ||
| 7757 | const AstEnumItem* prevp = firstp; // Prev must start with last item | ||
| 7758 | ✗ | while (prevp->nextp()) prevp = VN_AS(prevp->nextp(), EnumItem); | |
| 7759 | ✗ | for (AstEnumItem* itemp = firstp; itemp;) { | |
| 7760 | ✗ | AstEnumItem* const nextp = VN_AS(itemp->nextp(), EnumItem); | |
| 7761 | ✗ | const AstConst* const vconstp = VN_AS(itemp->valuep(), Const); | |
| 7762 | ✗ | UASSERT_OBJ(vconstp, nodep, "Enum item without constified value"); | |
| 7763 | ✗ | if (!vconstp->num().isAnyXZ()) { // Can 2-state runtime decode | |
| 7764 | ✗ | const uint64_t i = vconstp->toUQuad(); | |
| 7765 | ✗ | if (attrType == VAttrType::ENUM_NAME) { | |
| 7766 | ✗ | values[i] = new AstConst{nodep->fileline(), AstConst::String{}, | |
| 7767 | ✗ | itemp->name()}; | |
| 7768 | ✗ | } else if (attrType == VAttrType::ENUM_NEXT) { | |
| 7769 | ✗ | values[i] | |
| 7770 | ✗ | = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const | |
| 7771 | ✗ | } else if (attrType == VAttrType::ENUM_PREV) { | |
| 7772 | ✗ | values[i] = prevp->valuep()->cloneTree(false); // A const | |
| 7773 | ✗ | } else if (attrType == VAttrType::ENUM_VALID) { | |
| 7774 | ✗ | values[i] = new AstConst{nodep->fileline(), AstConst::BitTrue{}}; | |
| 7775 | } else { | ||
| 7776 | ✗ | nodep->v3fatalSrc("Bad case"); | |
| 7777 | } | ||
| 7778 | } | ||
| 7779 | prevp = itemp; | ||
| 7780 | itemp = nextp; | ||
| 7781 | } | ||
| 7782 | } | ||
| 7783 | // Add all specified values to table | ||
| 7784 | ✗ | if (assoc) { | |
| 7785 | ✗ | for (const auto& itr : values) initp->addIndexValuep(itr.first, itr.second); | |
| 7786 | } else { | ||
| 7787 | ✗ | for (uint64_t i = 0; i < (msbdim + 1); ++i) { | |
| 7788 | ✗ | if (values[i]) initp->addIndexValuep(i, values[i]); | |
| 7789 | } | ||
| 7790 | } | ||
| 7791 | varp->didWidth(true); // May have already done $unit so must do this var | ||
| 7792 | ✗ | pair.first->second = varp; | |
| 7793 | } | ||
| 7794 | ✗ | return pair.first->second; | |
| 7795 | } | ||
| 7796 | |||
| 7797 | ✗ | static AstNodeExpr* enumSelect(AstNodeExpr* nodep, AstEnumDType* adtypep, VAttrType attrType) { | |
| 7798 | // Return expression to get given attrType information from a enum's value (nodep) | ||
| 7799 | // Need a runtime lookup table. Yuk. | ||
| 7800 | ✗ | const uint64_t msbdim = enumMaxValue(nodep, adtypep); | |
| 7801 | const bool assoc = msbdim > ENUM_LOOKUP_BITS; | ||
| 7802 | AstNodeExpr* newp; | ||
| 7803 | ✗ | if (assoc) { | |
| 7804 | ✗ | AstVar* const varp = enumVarp(adtypep, attrType, true, 0); | |
| 7805 | ✗ | newp = new AstAssocSel{nodep->fileline(), newVarRefDollarUnit(varp), nodep}; | |
| 7806 | } else { | ||
| 7807 | ✗ | const int selwidth = V3Number::log2b(msbdim) + 1; // Width to address a bit | |
| 7808 | ✗ | AstVar* const varp = enumVarp(adtypep, attrType, false, (1ULL << selwidth) - 1); | |
| 7809 | newp = new AstArraySel{ | ||
| 7810 | ✗ | nodep->fileline(), newVarRefDollarUnit(varp), | |
| 7811 | // Select in case widths are off due to msblen!=width | ||
| 7812 | // We return "random" values if outside the range, which is fine | ||
| 7813 | // as next/previous on illegal values just need something good out | ||
| 7814 | ✗ | new AstSel{nodep->fileline(), nodep, 0, selwidth}}; | |
| 7815 | } | ||
| 7816 | ✗ | if (attrType == VAttrType::ENUM_NAME) { | |
| 7817 | ✗ | newp->dtypeSetString(); | |
| 7818 | ✗ | } else if (attrType == VAttrType::ENUM_VALID) { | |
| 7819 | ✗ | newp->dtypeSetBit(); | |
| 7820 | } else { | ||
| 7821 | newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE | ||
| 7822 | } | ||
| 7823 | ✗ | return newp; | |
| 7824 | } | ||
| 7825 | ✗ | static AstNodeExpr* enumTestValid(AstNodeExpr* valp, AstEnumDType* enumDtp) { | |
| 7826 | ✗ | const uint64_t maxval = enumMaxValue(valp, enumDtp); | |
| 7827 | const bool assoc = maxval > ENUM_LOOKUP_BITS; | ||
| 7828 | AstNodeExpr* testp = nullptr; | ||
| 7829 | ✗ | FileLine* const fl_novalue = new FileLine{valp->fileline()}; | |
| 7830 | ✗ | fl_novalue->warnOff(V3ErrorCode::ENUMVALUE, true); | |
| 7831 | ✗ | fl_novalue->warnOff(V3ErrorCode::CMPCONST, true); | |
| 7832 | ✗ | if (assoc) { | |
| 7833 | ✗ | AstVar* const varp = enumVarp(enumDtp, VAttrType::ENUM_VALID, true, 0); | |
| 7834 | ✗ | testp = new AstAssocSel{fl_novalue, newVarRefDollarUnit(varp), valp}; | |
| 7835 | } else { | ||
| 7836 | ✗ | const int selwidth = V3Number::log2b(maxval) + 1; // Width to address a bit | |
| 7837 | AstVar* const varp | ||
| 7838 | ✗ | = enumVarp(enumDtp, VAttrType::ENUM_VALID, false, (1ULL << selwidth) - 1); | |
| 7839 | ✗ | FileLine* const fl_nowidth = new FileLine{fl_novalue}; | |
| 7840 | ✗ | fl_nowidth->warnOff(V3ErrorCode::WIDTH, true); | |
| 7841 | testp = new AstCond{ | ||
| 7842 | fl_novalue, | ||
| 7843 | new AstGt{fl_nowidth, valp->cloneTreePure(false), | ||
| 7844 | ✗ | new AstConst{fl_nowidth, AstConst::Unsized64{}, maxval}}, | |
| 7845 | ✗ | new AstConst{fl_novalue, AstConst::BitFalse{}}, | |
| 7846 | ✗ | new AstArraySel{fl_novalue, newVarRefDollarUnit(varp), | |
| 7847 | ✗ | new AstSel{fl_novalue, valp->cloneTreePure(false), 0, selwidth}}}; | |
| 7848 | } | ||
| 7849 | ✗ | return testp; | |
| 7850 | } | ||
| 7851 | |||
| 7852 | ✗ | PatVecMap patVectorMap(AstPattern* nodep, const VNumRange& range) { | |
| 7853 | PatVecMap patmap; | ||
| 7854 | ✗ | int element = range.left(); | |
| 7855 | ✗ | for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; | |
| 7856 | ✗ | patp = VN_AS(patp->nextp(), PatMember)) { | |
| 7857 | ✗ | if (patp->keyp()) { | |
| 7858 | ✗ | if (patp->varrefp()) V3Const::constifyParamsEdit(patp->varrefp()); | |
| 7859 | if (const AstConst* const constp = VN_CAST(patp->keyp(), Const)) { | ||
| 7860 | ✗ | element = constp->toSInt(); | |
| 7861 | } else if (const AstConst* const constp = VN_CAST(patp->varrefp(), Const)) { | ||
| 7862 | ✗ | element = constp->toSInt(); | |
| 7863 | } else { | ||
| 7864 | ✗ | patp->keyp()->v3error("Assignment pattern key not supported/understood: " | |
| 7865 | << patp->keyp()->prettyTypeName()); | ||
| 7866 | } | ||
| 7867 | } | ||
| 7868 | ✗ | const bool newEntry = patmap.emplace(element, patp).second; | |
| 7869 | ✗ | if (!newEntry) { | |
| 7870 | ✗ | patp->v3error("Assignment pattern key used multiple times: " << element); | |
| 7871 | } | ||
| 7872 | ✗ | element += range.leftToRightInc(); | |
| 7873 | } | ||
| 7874 | ✗ | return patmap; | |
| 7875 | } | ||
| 7876 | |||
| 7877 | ✗ | void patConcatConvertRecurse(AstPattern* patternp, AstConcat* nodep) { | |
| 7878 | if (AstConcat* lhsp = VN_CAST(nodep->lhsp(), Concat)) { | ||
| 7879 | ✗ | patConcatConvertRecurse(patternp, lhsp); | |
| 7880 | } else { | ||
| 7881 | AstPatMember* const newp = new AstPatMember{ | ||
| 7882 | ✗ | nodep->lhsp()->fileline(), nodep->lhsp()->unlinkFrBack(), nullptr, nullptr}; | |
| 7883 | newp->isConcat(true); | ||
| 7884 | patternp->addItemsp(newp); | ||
| 7885 | } | ||
| 7886 | if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) { | ||
| 7887 | patConcatConvertRecurse(patternp, rhsp); | ||
| 7888 | } else { | ||
| 7889 | AstPatMember* const newp = new AstPatMember{ | ||
| 7890 | ✗ | nodep->rhsp()->fileline(), nodep->rhsp()->unlinkFrBack(), nullptr, nullptr}; | |
| 7891 | newp->isConcat(true); | ||
| 7892 | patternp->addItemsp(newp); | ||
| 7893 | } | ||
| 7894 | } | ||
| 7895 | |||
| 7896 | ✗ | void makeOpenArrayShell(AstNodeFTaskRef* nodep) { | |
| 7897 | ✗ | UINFO(4, "Replicate openarray function " << nodep->taskp() << endl); | |
| 7898 | AstNodeFTask* const oldTaskp = nodep->taskp(); | ||
| 7899 | oldTaskp->dpiOpenParentInc(); | ||
| 7900 | ✗ | UASSERT_OBJ(!oldTaskp->dpiOpenChild(), oldTaskp, | |
| 7901 | "DPI task should be parent or child, not both"); | ||
| 7902 | AstNodeFTask* const newTaskp = oldTaskp->cloneTree(false); | ||
| 7903 | newTaskp->dpiOpenChild(true); | ||
| 7904 | newTaskp->dpiOpenParentClear(); | ||
| 7905 | ✗ | newTaskp->name(newTaskp->name() + "__Vdpioc" + cvtToStr(oldTaskp->dpiOpenParent())); | |
| 7906 | ✗ | oldTaskp->addNextHere(newTaskp); | |
| 7907 | // Relink reference to new function | ||
| 7908 | nodep->taskp(newTaskp); | ||
| 7909 | ✗ | nodep->name(nodep->taskp()->name()); | |
| 7910 | // Replace open array arguments with the callee's task | ||
| 7911 | ✗ | const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); | |
| 7912 | ✗ | for (const auto& tconnect : tconnects) { | |
| 7913 | ✗ | AstVar* const portp = tconnect.first; | |
| 7914 | ✗ | const AstArg* const argp = tconnect.second; | |
| 7915 | const AstNode* const pinp = argp->exprp(); | ||
| 7916 | ✗ | if (!pinp) continue; // Argument error we'll find later | |
| 7917 | ✗ | if (hasOpenArrayIterateDType(portp->dtypep())) portp->dtypep(pinp->dtypep()); | |
| 7918 | } | ||
| 7919 | } | ||
| 7920 | |||
| 7921 | ✗ | bool markHasOpenArray(AstNodeFTask* nodep) { | |
| 7922 | bool hasOpen = false; | ||
| 7923 | ✗ | for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { | |
| 7924 | if (AstVar* const portp = VN_CAST(stmtp, Var)) { | ||
| 7925 | ✗ | if (portp->isDpiOpenArray() || hasOpenArrayIterateDType(portp->dtypep())) { | |
| 7926 | portp->isDpiOpenArray(true); | ||
| 7927 | hasOpen = true; | ||
| 7928 | } | ||
| 7929 | } | ||
| 7930 | } | ||
| 7931 | ✗ | return hasOpen; | |
| 7932 | } | ||
| 7933 | ✗ | bool hasOpenArrayIterateDType(AstNodeDType* nodep) { | |
| 7934 | // Return true iff this datatype or child has an openarray | ||
| 7935 | if (VN_IS(nodep, UnsizedArrayDType)) return true; | ||
| 7936 | ✗ | if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp()); | |
| 7937 | return false; | ||
| 7938 | } | ||
| 7939 | |||
| 7940 | //---------------------------------------------------------------------- | ||
| 7941 | // METHODS - special type detection | ||
| 7942 | |||
| 7943 | 173382 | void assertAtStatement(AstNode* nodep) { | |
| 7944 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 173382 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
173382 | if (VL_UNCOVERABLE(m_vup && !m_vup->selfDtm())) { |
| 7945 | ✗ | UINFO(1, "-: " << m_vup << endl); | |
| 7946 | ✗ | nodep->v3fatalSrc("No dtype expected at statement " << nodep->prettyTypeName()); | |
| 7947 | } | ||
| 7948 | 173382 | } | |
| 7949 |
1/2✓ Branch 0 taken 155568 times.
✗ Branch 1 not taken.
|
155568 | void checkConstantOrReplace(AstNode* nodep, const string& message) { |
| 7950 | // See also V3WidthSel::checkConstantOrReplace | ||
| 7951 | // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us! | ||
| 7952 | if (!VN_IS(nodep, Const)) { | ||
| 7953 | ✗ | nodep->v3error(message); | |
| 7954 | ✗ | nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::Unsized32{}, 1}); | |
| 7955 | VL_DO_DANGLING(pushDeletep(nodep), nodep); | ||
| 7956 | } | ||
| 7957 | 155568 | } | |
| 7958 | ✗ | static AstVarRef* newVarRefDollarUnit(AstVar* nodep) { | |
| 7959 | ✗ | AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), nodep, VAccess::READ}; | |
| 7960 | ✗ | varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); | |
| 7961 | ✗ | return varrefp; | |
| 7962 | } | ||
| 7963 | ✗ | AstNode* nodeForUnsizedWarning(AstNode* nodep) { | |
| 7964 | // Return a nodep to use for unsized warnings, reporting on child if can | ||
| 7965 | ✗ | if (nodep->op1p() && nodep->op1p()->dtypep() && !nodep->op1p()->dtypep()->widthSized()) { | |
| 7966 | return nodep->op1p(); | ||
| 7967 | ✗ | } else if (nodep->op2p() && nodep->op2p()->dtypep() | |
| 7968 | ✗ | && !nodep->op2p()->dtypep()->widthSized()) { | |
| 7969 | return nodep->op2p(); | ||
| 7970 | } | ||
| 7971 | return nodep; // By default return this | ||
| 7972 | } | ||
| 7973 | ✗ | AstRefDType* checkRefToTypedefRecurse(AstNode* nodep, AstTypedef* typedefp) { | |
| 7974 | // Recurse all children looking for self reference | ||
| 7975 | // This avoids iterateEditMoveDTypep going into a hard to resolve loop | ||
| 7976 | // Only call once for any given typedef, or will become O(n^2) | ||
| 7977 | ✗ | if (VL_LIKELY(!nodep)) return nullptr; | |
| 7978 | if (auto* const refp = VN_CAST(nodep, RefDType)) { | ||
| 7979 | ✗ | if (refp->typedefp() == typedefp) return refp; | |
| 7980 | } | ||
| 7981 | ✗ | if (auto* const refp = checkRefToTypedefRecurse(nodep->op1p(), typedefp)) return refp; | |
| 7982 | ✗ | if (auto* const refp = checkRefToTypedefRecurse(nodep->op2p(), typedefp)) return refp; | |
| 7983 | ✗ | if (auto* const refp = checkRefToTypedefRecurse(nodep->op3p(), typedefp)) return refp; | |
| 7984 | ✗ | if (auto* const refp = checkRefToTypedefRecurse(nodep->op4p(), typedefp)) return refp; | |
| 7985 | return nullptr; | ||
| 7986 | } | ||
| 7987 | |||
| 7988 | //---------------------------------------------------------------------- | ||
| 7989 | // METHODS - special iterators | ||
| 7990 | // These functions save/restore the AstNUser information so it can pass to child nodes. | ||
| 7991 | |||
| 7992 | AstNode* userIterateSubtreeReturnEdits(AstNode* nodep, WidthVP* vup) { | ||
| 7993 |
3/18✓ Branch 0 taken 512555 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 159866 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 641281 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
|
1313702 | if (!nodep) return nullptr; |
| 7994 | AstNode* ret; | ||
| 7995 | { | ||
| 7996 | VL_RESTORER(m_vup); | ||
| 7997 | 2606677 | m_vup = vup; | |
| 7998 |
7/38✓ Branch 1 taken 512555 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 819347 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 105862 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 133885 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 233881 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 34 taken 159866 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 641281 times.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
2606677 | ret = iterateSubtreeReturnEdits(nodep); |
| 7999 | } | ||
| 8000 | 672421 | return ret; | |
| 8001 | } | ||
| 8002 | void userIterate(AstNode* nodep, WidthVP* vup) { | ||
| 8003 |
4/40✓ Branch 0 taken 80095 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 79812 times.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 90017 times.
✓ Branch 39 taken 90017 times.
|
339941 | if (!nodep) return; |
| 8004 | VL_RESTORER(m_vup); | ||
| 8005 |
3/70✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 80095 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 884 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✓ Branch 103 taken 90017 times.
✗ Branch 104 not taken.
|
170996 | m_vup = vup; |
| 8006 | iterate(nodep); | ||
| 8007 | } | ||
| 8008 | 1908015 | void userIterateAndNext(AstNode* nodep, WidthVP* vup) { | |
| 8009 |
2/2✓ Branch 0 taken 1821457 times.
✓ Branch 1 taken 86558 times.
|
1908015 | if (!nodep) return; |
| 8010 |
2/2✓ Branch 0 taken 1290420 times.
✓ Branch 1 taken 531037 times.
|
1821457 | if (nodep->didWidth()) return; // Avoid iterating list we have already iterated |
| 8011 |
1/2✓ Branch 1 taken 1290420 times.
✗ Branch 2 not taken.
|
1290420 | VL_RESTORER(m_vup); |
| 8012 | 1290420 | m_vup = vup; | |
| 8013 |
1/2✓ Branch 1 taken 1290420 times.
✗ Branch 2 not taken.
|
1290420 | iterateAndNextNull(nodep); |
| 8014 | } | ||
| 8015 | void userIterateChildren(AstNode* nodep, WidthVP* vup) { | ||
| 8016 |
2/32✓ Branch 0 taken 32238 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 41188 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
|
73426 | if (!nodep) return; |
| 8017 | VL_RESTORER(m_vup); | ||
| 8018 | 78326 | m_vup = vup; | |
| 8019 |
3/80✓ Branch 1 taken 32238 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 16 taken 4900 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 41188 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
|
78326 | iterateChildren(nodep); |
| 8020 | } | ||
| 8021 | void userIterateChildrenBackwardsConst(AstNode* nodep, WidthVP* vup) { | ||
| 8022 |
1/2✓ Branch 0 taken 485 times.
✗ Branch 1 not taken.
|
485 | if (!nodep) return; |
| 8023 | VL_RESTORER(m_vup); | ||
| 8024 | 485 | m_vup = vup; | |
| 8025 |
1/2✓ Branch 1 taken 485 times.
✗ Branch 2 not taken.
|
485 | iterateChildrenBackwardsConst(nodep); |
| 8026 | } | ||
| 8027 | |||
| 8028 | public: | ||
| 8029 | // CONSTRUCTORS | ||
| 8030 | 512555 | WidthVisitor(bool paramsOnly, // [in] TRUE if we are considering parameters only. | |
| 8031 | bool doGenerate) // [in] TRUE if we are inside a generate statement and | ||
| 8032 | // // don't wish to trigger errors | ||
| 8033 | 512555 | : m_paramsOnly{paramsOnly} | |
| 8034 | 512555 | , m_doGenerate{doGenerate} {} | |
| 8035 | 512555 | AstNode* mainAcceptEdit(AstNode* nodep) { | |
| 8036 | 1025110 | return userIterateSubtreeReturnEdits(nodep, WidthVP{SELF, BOTH}.p()); | |
| 8037 | } | ||
| 8038 | 1025110 | ~WidthVisitor() override = default; | |
| 8039 | }; | ||
| 8040 | |||
| 8041 | //###################################################################### | ||
| 8042 | // Width class functions | ||
| 8043 | |||
| 8044 | 485 | void V3Width::width(AstNetlist* nodep) { | |
| 8045 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 485 times.
|
485 | UINFO(2, __FUNCTION__ << ": " << endl); |
| 8046 | { | ||
| 8047 | // We should do it in bottom-up module order, but it works in any order. | ||
| 8048 | const WidthClearVisitor cvisitor{nodep}; | ||
| 8049 | 485 | WidthVisitor visitor{false, false}; | |
| 8050 |
1/2✓ Branch 1 taken 485 times.
✗ Branch 2 not taken.
|
485 | (void)visitor.mainAcceptEdit(nodep); |
| 8051 | WidthRemoveVisitor rvisitor; | ||
| 8052 | (void)rvisitor.mainAcceptEdit(nodep); | ||
| 8053 | 485 | } // Destruct before checking | |
| 8054 |
1/2✓ Branch 3 taken 485 times.
✗ Branch 4 not taken.
|
485 | V3Global::dumpCheckGlobalTree("width", 0, dumpTreeEitherLevel() >= 3); |
| 8055 | 485 | } | |
| 8056 | //! Single node parameter propagation | ||
| 8057 | //! Smaller step... Only do a single node for parameter propagation | ||
| 8058 | 512070 | AstNode* V3Width::widthParamsEdit(AstNode* nodep) { | |
| 8059 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 512070 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
512070 | UINFO(4, __FUNCTION__ << ": " << nodep << endl); |
| 8060 | // We should do it in bottom-up module order, but it works in any order. | ||
| 8061 | 512070 | WidthVisitor visitor{true, false}; | |
| 8062 |
1/2✓ Branch 1 taken 512070 times.
✗ Branch 2 not taken.
|
512070 | nodep = visitor.mainAcceptEdit(nodep); |
| 8063 | // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks | ||
| 8064 | 512070 | return nodep; | |
| 8065 | 512070 | } | |
| 8066 | |||
| 8067 | //! Single node parameter propagation for generate blocks. | ||
| 8068 | //! Smaller step... Only do a single node for parameter propagation | ||
| 8069 | //! If we are inside a generated "if", "case" or "for", we don't want to | ||
| 8070 | //! trigger warnings when we deal with the width. It is possible that | ||
| 8071 | //! these are spurious, existing within sub-expressions that will not | ||
| 8072 | //! actually be generated. Since such occurrences, must be constant, in | ||
| 8073 | //! order to be something a generate block can depend on, we can wait until | ||
| 8074 | //! later to do the width check. | ||
| 8075 | //! @return Pointer to the edited node. | ||
| 8076 | ✗ | AstNode* V3Width::widthGenerateParamsEdit( | |
| 8077 | AstNode* nodep) { //!< [in] AST whose parameters widths are to be analyzed. | ||
| 8078 | ✗ | UINFO(4, __FUNCTION__ << ": " << nodep << endl); | |
| 8079 | // We should do it in bottom-up module order, but it works in any order. | ||
| 8080 | ✗ | WidthVisitor visitor{true, true}; | |
| 8081 | ✗ | nodep = visitor.mainAcceptEdit(nodep); | |
| 8082 | // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks | ||
| 8083 | ✗ | return nodep; | |
| 8084 | } | ||
| 8085 |